package com.yaodian100.ec.order.service.impl;

import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.betwixt.io.BeanWriter;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.tools.generic.NumberTool;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

import com.yaodian100.core.batch.service.BatchJobService;
import com.yaodian100.core.common.dao.ObjectDao;
import com.yaodian100.core.common.dao.impl.CommonCriteria;
import com.yaodian100.core.common.exception.CoreException;
import com.yaodian100.core.common.service.BaseManager;
import com.yaodian100.core.common.service.impl.DomainServiceImpl;
import com.yaodian100.core.common.utils.JspUtil;
import com.yaodian100.core.common.utils.ServerValue;
import com.yaodian100.core.form.entity.Attribute;
import com.yaodian100.core.mail.entity.Mail;
import com.yaodian100.core.mail.service.MailService;
import com.yaodian100.core.menu.entity.Option;
import com.yaodian100.core.menu.service.MenuService;
import com.yaodian100.core.sms.service.SmsService;
import com.yaodian100.core.template.service.TemplateService;
import com.yaodian100.crm.cs.entity.FromChannel;
import com.yaodian100.crm.cs.entity.Question;
import com.yaodian100.crm.cs.entity.QuestionExtraValue;
import com.yaodian100.crm.cs.entity.QuestionType;
import com.yaodian100.crm.workorder.entity.LogisticProvider;
import com.yaodian100.crm.workorder.service.WorkOrderService;
import com.yaodian100.ec.cobranding.Onlylady;
import com.yaodian100.ec.cobranding.service.DPNService;
import com.yaodian100.ec.common.EnvParameters;
import com.yaodian100.ec.coupon.service.CouponService;
import com.yaodian100.ec.delivery.service.DeliveryRuleService;
import com.yaodian100.ec.member.entity.Member;
import com.yaodian100.ec.member.entity.VirtualBalance;
import com.yaodian100.ec.member.service.MemberService;
import com.yaodian100.ec.order.entity.InboundTrackingCase;
import com.yaodian100.ec.order.entity.JobExecutedLog;
import com.yaodian100.ec.order.entity.OrderMemo;
import com.yaodian100.ec.order.entity.OrderProduct;
import com.yaodian100.ec.order.entity.OrderProductDetail;
import com.yaodian100.ec.order.entity.Orders;
import com.yaodian100.ec.order.entity.ServiceOrderDetail;
import com.yaodian100.ec.order.entity.ServiceOrderProduct;
import com.yaodian100.ec.order.entity.ServiceOrders;
import com.yaodian100.ec.order.remote.ODetail;
import com.yaodian100.ec.order.remote.ServiceOrderBo;
import com.yaodian100.ec.order.remote.ServiceProduct;
import com.yaodian100.ec.order.rule.Return;
import com.yaodian100.ec.order.service.OrderService;
import com.yaodian100.ec.order.service.ServiceOrderService;
import com.yaodian100.ec.payment.entity.ThirdPartyTransfer;
import com.yaodian100.ec.payment.service.TransferService;
import com.yaodian100.ec.product.entity.Product;
import com.yaodian100.ec.product.entity.Specification;
import com.yaodian100.ec.product.entity.SpecificationInventory;
import com.yaodian100.ec.product.service.ProductService;
import com.yaodian100.ec.shelf.entity.ShelfTuangou;
import com.yaodian100.ec.supplierapply.entity.SupplierApply;
import com.yaodian100.ec.supplierapply.service.SupplierApplyService;

public class ServiceOrderServiceImpl extends DomainServiceImpl<ServiceOrders> implements ServiceOrderService {
	private static Logger LOGGER = LoggerFactory.getLogger(ServiceOrderServiceImpl.class);
	private static final String[] IGNORE_PROPERTIES_WHEN_COPY_FROM_ORDER_DETAIL_TO_SERVICE_DETAIL = new String[] {
			"shippingOid", "shippingLineOid" };
	private static final String ORDER_STATUS_COMPLETE = "90";
	@Resource
	private BaseManager baseMgr;
	@Resource
	private BatchJobService batchJobService;
	@Resource
	private JdbcTemplate jdbcTemplate;
	@Resource
	private ProductService productService;
	@Resource
	private EnvParameters envParameters;
	@Resource
	private MailService mailService;
	@Resource
	private MemberService memberService;
	@Resource
	private MenuService menuService;
	@Resource
	private ObjectDao<ServiceOrderDetail> serviceOrderDetailDao;
	@Resource
	private ObjectDao<ServiceOrderProduct> serviceOrderProductDao;
	@Resource
	private ObjectDao<OrderProductDetail> orderProductDetailDao;
	@Resource
	private TemplateService velocityService;
	@Resource
	private WorkOrderService workOrderService;
	@Resource
	private TransferService transferService;
	@Resource
	private OrderService orderService;

	@Resource
	private SmsService smsService;
	@Resource
	private DPNService dpnService;

	@Resource
	private DeliveryRuleService deliveryRuleService;

	@Resource
	private CouponService couponService;
	@Resource
	private SupplierApplyService supplierApplyService;

	@Resource
	private ObjectDao<InboundTrackingCase> inboundTrackingCaseDao;

	@Transactional(readOnly = false)
	private ServiceOrders cancelOrder(Orders order, String description, String reason, String memberId, String clientIp,
			String b2eId, String userId, String reasonTp, boolean returnCoupon) {
		if (userId == null) {
			userId = "SYSTEM";
		}
		ServiceOrders serviceOrder = new ServiceOrders();
		serviceOrder.setReceiverEmail(order.getReceiverEmail());

		serviceOrder.setReasonTp(reasonTp);
		BeanUtils.copyProperties(order, serviceOrder);
		serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.CANCEL.getValue());
		serviceOrder.setAccessaryDescription("无");
		serviceOrder.setDescription(description);
		serviceOrder.setReason(reason);
		Date createDate = new Date();
		serviceOrder.setCreateDate(createDate);
		serviceOrder.setCreateUser(userId);
		Member member = null;
		member = (Member) baseMgr.get(Member.class, memberId);
		serviceOrder.setLoginId(member.getLoginId());
		serviceOrder.setMemberId(member.getMemberId());
		serviceOrder.setMemberName(member.getName());
		serviceOrder.setMemo(" ");
		serviceOrder.setOrderSt("10");
		serviceOrder.setReturnSt("10");
		serviceOrder.setModifyUser(userId);
		serviceOrder.setServiceDt(createDate);

		serviceOrder.setReceiverEmail(member.getEmail());
		serviceOrder.setOrderId(order.getOrderId());
		serviceOrder.setClientIp(clientIp);
		serviceOrder.setB2eId(b2eId);
		serviceOrder.setReturnCodFee(order.getCodFee());
		serviceOrder.setRealCodFee(order.getCodFee());
		// 整张取消
		Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();

		for (OrderProduct op : order.getOrderProduct()) {
			ServiceOrderProduct sop = new ServiceOrderProduct();
			BeanUtils.copyProperties(op, sop);
			sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
			for (OrderProductDetail opd : op.getOrderProductDetail()) {
				for (int i = 0; i < opd.getQty(); i++) {
					// 所有serviceOrder qty=1
					ServiceOrderDetail sod = new ServiceOrderDetail();
					BeanUtils.copyProperties(opd, sod);
					sod.setOrgDetailSeq(sod.getDetailSeq());
					sod.setOrgProdSpecId(sod.getProdSpecId());
					sod.setOrgProdSpecName(sod.getProdSpecName());
					sod.setQty(1);
					sop.getServiceOrderDetail().add(sod);
				}
			}
			serviceOrderProducts.add(sop);
			// }
		}

		// 201107.依照財務的要求，有退費時分期手續費將不被計算再realcash和returncash中
		BigDecimal realCash = order.getCash();
		BigDecimal returnCash = order.getCash();
		if (order.getCash().compareTo(BigDecimal.ZERO) > 0 && "80".equals(order.getPaymentTp())) {
			realCash = new BigDecimal(order.getCash().longValue() - order.getInstallmentFee().longValue());
			returnCash = new BigDecimal(order.getCash().longValue() - order.getInstallmentFee().longValue());
		}

		serviceOrder.setReturnBenefit(order.getBenefit());
		serviceOrder.setReturnBonus(order.getBonus());
		serviceOrder.setReturnCash(returnCash);
		serviceOrder.setReturnMyWallet(order.getMyWallet());
		serviceOrder.setReturnShipfare(order.getShipfare());
		serviceOrder.setReturnCodFee(order.getCodFee());
		serviceOrder.setRealBenefit(order.getBenefit());
		serviceOrder.setRealBonus(order.getBonus());
		serviceOrder.setRealCash(realCash);
		serviceOrder.setRealMyWallet(order.getMyWallet());
		serviceOrder.setRealShipfare(order.getShipfare());
		serviceOrder.setRealCodFee(order.getCodFee());
		serviceOrder.setOrderType(order.getOrderType());
		serviceOrder.setReturnGiftcardBalance(order.getGiftcardBalance());// 20110919 by feeling(giftcard)
		serviceOrder.setRealGiftcardBalance(order.getGiftcardBalance());// 20110919 by feeling(giftcard)
		// 退分期手續費，如果cash是0代表沒收錢，以order的部份來說有cash才有可能會產生installmentfee
		if (order.getCash().compareTo(BigDecimal.ZERO) > 0 && "80".equals(order.getPaymentTp())) {
			serviceOrder.setReturnInstallmentFee(order.getInstallmentFee());
			serviceOrder.setRealInstallmentFee(order.getInstallmentFee());
		} else {
			serviceOrder.setReturnInstallmentFee(BigDecimal.ZERO);
			serviceOrder.setRealInstallmentFee(BigDecimal.ZERO);
		}

		serviceOrder.setScmSupplierId(order.getScmSupplierId());
		if (returnCoupon) {
			serviceOrder.setReturnCoupon(order.getCoupon());
			serviceOrder.setRealCoupon(order.getCoupon());
		} else { // 部分取消不退coupon状况
			serviceOrder.setReturnCoupon(BigDecimal.ZERO);
			serviceOrder.setRealCoupon(BigDecimal.ZERO);
		}

		// 8号单，全加至mywallet
		if (serviceOrder.getReason().equals("客服修改订单")) {
			serviceOrder.setReturnMyWallet(serviceOrder.getReturnMyWallet().add(serviceOrder.getReturnCash()));
			serviceOrder.setRealMyWallet(serviceOrder.getRealMyWallet().add(serviceOrder.getRealCash()));
			serviceOrder.setReturnCash(BigDecimal.ZERO);
			serviceOrder.setRealCash(BigDecimal.ZERO);
		}
		// 檢查金額是否有問題
		checkServiceOrderAmount(serviceOrder, order);
		boolean mustSyncERP = false;
		if (ServiceOrders.REASON_TYPE.CSR_UPDATEORDER.getValue().equals(reasonTp)) {
			// 将订单金额退至账户余额
			// 更新待付款訂單數
			memberService.updateOrder2PayBalance(order.getMemberId());
			// 未付款：取消订单退款
			serviceOrder.setRefundApproveDate(new Date());
			serviceOrder.setRefundDate(serviceOrder.getRefundApproveDate());
			order.setRefundDate(serviceOrder.getRefundDate());
			refundForNotPaid(order);
			// csr修改订单，同步8号单

			// 取消單狀態直接為90
			serviceOrder.setOrderSt("90");
			// 退款狀態直接為30
			serviceOrder.setReturnSt("30");
			mustSyncERP = true;
			serviceOrder.setRefundApproveDate(new Date());
			serviceOrder.setRefundDate(serviceOrder.getRefundApproveDate());
		} else if (order.getPaymentSt().equals("10")) {
			mustSyncERP = true;
		} else if (order.getPaymentSt().equals("00")) {
			// 不需後送的取消單，直接將退款回寫至訂單
			updateOrderReturn(order.getOrderId(), serviceOrder.getRealBenefit().floatValue(), serviceOrder.getRealCash()
					.floatValue(), serviceOrder.getRealMyWallet().floatValue(), serviceOrder.getRealBonus().floatValue(),
					serviceOrder.getRealCoupon().floatValue(), serviceOrder.getRealInstallmentFee().floatValue(), serviceOrder
							.getRealGiftcardBalance().floatValue(), serviceOrder.getRealShipfare().floatValue(), new Date());
			// 未付款
			serviceOrder.setOrderSt("90");
			// 退款狀態直接為30
			serviceOrder.setReturnSt("30");
			mustSyncERP = false;
			// 不需同步的
			// 更新待付款訂單數
			memberService.updateOrder2PayBalance(order.getMemberId());
			// 未付款：取消订单退款
			refundForNotPaid(order);

		}
		try {

			updateOrder(serviceOrder, serviceOrderProducts);
			this.saveServiceOrders(order, serviceOrder, serviceOrderProducts, mustSyncERP);

		} catch (CoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			String domainSrc = order.getDomainSrc();
			if (!domainSrc.endsWith("yaodian100.com")) {
				if (domainSrc.endsWith("onlylady.com")) {
					Onlylady ol = new Onlylady();
					ol.olCancelOrder(order);
				}
			}
		} catch (Exception e) {

			// e.printStackTrace();
		}
		// sendMail("member.cancelOrder", "订单" + order.getOrderId() + " 取消成功", order);
		return serviceOrder;

	}

	@Transactional(readOnly = false)
	public ServiceOrders cancelOrderBySystem(String orderId, String description, String reason, String memberId,
			String clientIp, String b2eId) {
		Orders order = (Orders) baseMgr.get(Orders.class, orderId);

		// 訂單取消
		if (order.isSync()) {
			throw new CoreException("订单已送出无法取消，请改退货处理");
		}
		// if (!"10".equals(order.getOrderSt())) {
		// throw new CoreException("此订单单状态不允许取消");
		// }
		if (!Orders.ORDER_ST.CANCEL_BY_NOT_APPROVE.getValue().equals(order.getOrderSt())) {
			order.setOrderSt(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue());
		}
		// 团购订单要在取消时解开团购锁定
		if (!order.isTuangouSync() && order.getTuangouId() != null) {
			order.setTuangouSync(true);
		}
		baseMgr.update(order);
		String userId = "SYSTEM";
		String reasonTp = ServiceOrders.REASON_TYPE.SYSTEM_CANCEL.getValue();
		return cancelOrder(order, description, reason, memberId, clientIp, b2eId, userId, reasonTp, true);

	}

	@Transactional(readOnly = false)
	public ServiceOrders cancelOrderByCsrUpdateOrder(Orders order, String description, String reason, String memberId,
			String clientIp, String b2eId, String userId, List<OrderProduct> cancelProducts) {
		// 退回coupon
		boolean returnCoupon = false;
		if (cancelProducts != null) {
			for (OrderProduct op : cancelProducts) {
				if (op.getCouponId() != null) {
					// couponService.unuseCoupon(order, op.getCouponId());
					returnCoupon = true;
				}
			}
		}
		// 退回
		returnInventory(order);
		order.setOrderSt(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue());

		String reasonTp = ServiceOrders.REASON_TYPE.CSR_UPDATEORDER.getValue();
		return cancelOrder(order, description, reason, memberId, clientIp, b2eId, userId, reasonTp, returnCoupon);
	}

	@Transactional(readOnly = false)
	public ServiceOrders cancelOrderByUser(String orderId, String description, String reason, String memberId,
			String clientIp, String b2eId) {
		Orders order = (Orders) baseMgr.get(Orders.class, orderId);
		if (order.getJoinOrders().equals(orderId)) {
			if (order.getOrderSt().equals(Orders.ORDER_ST.CANCEL_BY_USER.getValue())
					|| order.getOrderSt().equals(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue())) {
				throw new CoreException("訂單已取消！");
			}
			// 訂單取消
			if (order.isSync()) {
				throw new CoreException("订单已送出无法取消，请改退货处理");
			}
			// 退回
			returnInventory(order);
			// if (!"10".equals(order.getOrderSt())) {
			// throw new CoreException("此订单单状态不允许取消");
			// }
			order.setOrderSt(Orders.ORDER_ST.CANCEL_BY_USER.getValue());
			// 团购订单要在取消时解开团购锁定
			if (!order.isTuangouSync() && order.getTuangouId() != null) {
				order.setTuangouSync(true);
			}

			baseMgr.update(order);
			String userId = "SYSTEM";
			String reasonTp = ServiceOrders.REASON_TYPE.USER_CANCEL.getValue();
			return cancelOrder(order, description, reason, memberId, clientIp, b2eId, userId, reasonTp, true);
		} else {
			// 有拆单, 逐笔取消
			for (String o : StringUtils.split(order.getJoinOrders(), ",")) {
				order = (Orders) baseMgr.get(Orders.class, o);
				if (order.getOrderSt().equals(Orders.ORDER_ST.CANCEL_BY_USER.getValue())
						|| order.getOrderSt().equals(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue())) {
					throw new CoreException("訂單已取消！");
				}
				// 訂單取消
				if (order.isSync()) {
					throw new CoreException("订单已送出无法取消，请改退货处理");
				}
				// 退回
				returnInventory(order);

				// if (!"10".equals(order.getOrderSt())) {
				// throw new CoreException("此订单单状态不允许取消");
				// }
				order.setOrderSt(Orders.ORDER_ST.CANCEL_BY_USER.getValue());
				baseMgr.update(order);
				String userId = "SYSTEM";
				String reasonTp = ServiceOrders.REASON_TYPE.USER_CANCEL.getValue();
				cancelOrder(order, description, reason, memberId, clientIp, b2eId, userId, reasonTp, true);
			}
			return null;
		}
	}

	@Transactional(readOnly = false)
	public String cancelServiceOrderByERP(String serviceId) {
		ServiceOrders so = this.get(serviceId);

		if (null != so) {
			// 退货单(srcType=1)+状态审核中,处理中(orderSt=10,00)
			if (("10".equals(so.getOrderSt()) || "00".equals(so.getOrderSt())) && so.getSrcType() == 1) {
				so.setOrderSt(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue());
				Date currentDate = new Date();
				so.setConfirmDt(currentDate);
				baseMgr.update(so);
				if ("00".equals(so.getOrderSt())) {// 审核中要再去结工单
					workOrderService.closeOrderForReturnProductApply(serviceId, "SYSTEM");
				}
				return "true";
			} else {
				return "服务单的类型非退货单或服务单的状态非审核中或处理中";
			}
		} else {
			return "服务单不存在";
		}

	}

	@Transactional(readOnly = false)
	public void cancelReturnProduct(String serviceId) {
		ServiceOrders so = (ServiceOrders) baseMgr.get(ServiceOrders.class, serviceId);
		so.setOrderSt(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue());
		Date currentDate = new Date();
		so.setConfirmDt(currentDate);
		baseMgr.update(so);
	}

	@Transactional(readOnly = false)
	public void confirmReturnProduct(String serviceId) {

		ServiceOrders so = (ServiceOrders) baseMgr.get(ServiceOrders.class, serviceId);
		so.setOrderSt(Orders.ORDER_ST.ORDER_CREATE.getValue());
		Date currentDate = new Date();
		so.setConfirmDt(currentDate);
		baseMgr.update(so);
		Object[] sops = baseMgr
				.queryByHql("from ServiceOrderProduct where serviceId=?", new Object[] { so.getServiceId() });
		List list = new ArrayList();
		for (Object obj : sops) {
			list.add(obj);
		}
		so.setServiceOrderProducts(list);
		batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(so) + "</root>", null, so
				.getServiceId());
		// 外部商城订单不寄发简讯及mail
		if (StringUtils.isBlank(so.getOuterOrderId())) {
			Orders order = (Orders) baseMgr.get(Orders.class, so.getOrderId());
			Member member = (Member) baseMgr.get(Member.class, order.getMemberId());
			// send sms
			if (member.getMobile() != null && member.getMobile().length() > 0) {
				String msg = "您好，您在耀点100的订单" + order.getOrderId() + "，退货申请已通过，请于3日内将商品寄回，我们会尽快验收并将退款退回您的耀点100账户中。";
				smsService.sendSms(member.getMobile(), msg, order.getOrderId());
			}
			this.sendMailForReturnProductConfirm(order, so, sops);
		}
	}

	public void createCancelOrderForSyncOrder(String orderId, String description, String reason, String memberId,
			String clientIp, String b2eId) {
		Orders order = (Orders) baseMgr.get(Orders.class, orderId);
		String userId = "SYSTEM";
		String reasonTp = ServiceOrders.REASON_TYPE.USER_CANCEL.getValue();
		cancelOrder(order, description, reason, memberId, clientIp, b2eId, userId, reasonTp, true);
	}

	public List<ServiceOrderDetail> getServiceOrderDetailList(int firstResult, int maxResults, CommonCriteria criteria,
			String[] sortOrder) throws CoreException {
		String[] defaultSort = new String[] { "serviceId", "orderId", "prodSeq", "detailSeq asc" };
		if (sortOrder == null) {
			return serviceOrderDetailDao.getListPageable(criteria, defaultSort, firstResult, maxResults);
		}
		return serviceOrderDetailDao.getListPageable(criteria, sortOrder, firstResult, maxResults);
	}

	public List<ServiceOrderProduct> getServiceOrderProdList(int firstResult, int maxResults, CommonCriteria criteria,
			String[] sortOrder) throws CoreException {
		String[] defaultSort = new String[] { "serviceId", "orderId", "prodSeq" };
		if (sortOrder == null) {
			return serviceOrderProductDao.getListPageable(criteria, defaultSort, firstResult, maxResults);
		}
		return serviceOrderProductDao.getListPageable(criteria, sortOrder, firstResult, maxResults);
	}

	public List<ServiceOrders> getServiceOrderListByOrderProductId(Long orderProdId) {
		List<ServiceOrders> result = new ArrayList<ServiceOrders>();

		CommonCriteria criteria = new CommonCriteria();
		criteria.addEq("orderProdId", orderProdId);
		List<ServiceOrderProduct> queryResult = this.serviceOrderProductDao
				.getList(criteria, new String[] { "createDate" });
		for (ServiceOrderProduct serviceOrderProduct : queryResult) {
			ServiceOrders serviceOrder = this.getDao().get(serviceOrderProduct.getServiceId());
			result.add(serviceOrder);
		}

		return result;
	}

	public List<ServiceOrders> getServiceOrderListByOrderDetailId(Long orderDetailId) {
		List<ServiceOrders> result = new ArrayList<ServiceOrders>();

		CommonCriteria criteria = new CommonCriteria();
		criteria.addEq("orderDetailId", orderDetailId);
		List<ServiceOrderDetail> queryResult = this.serviceOrderDetailDao.getList(criteria, new String[] { "createDate" });
		for (ServiceOrderDetail oneServiceOrderDetail : queryResult) {
			ServiceOrders serviceOrder = this.getDao().get(oneServiceOrderDetail.getServiceId());
			result.add(serviceOrder);
		}

		return result;
	}

	public ServiceOrderDetail latestServiceOrderDetailByOrderDetailId(Long orderDetailId) {
		CommonCriteria criteria = new CommonCriteria();
		criteria.addEq("orderDetailId", orderDetailId);
		List<ServiceOrderDetail> queryResult = this.serviceOrderDetailDao.getList(criteria, new String[] { "createDate" });
		if (queryResult == null || queryResult.size() <= 0) {
			return null;
		}
		ServiceOrderDetail serviceOrderDetail = queryResult.get(0);
		return serviceOrderDetail;
	}

	/**
	 * 判斷某個 orderDetailId 是否可以做退換貨
	 */
	public boolean isOrderDetailReturnableChangable(Long orderDetailId) {
		ServiceOrderDetail serviceOrderDetail = latestServiceOrderDetailByOrderDetailId(orderDetailId);
		if (serviceOrderDetail == null) {
			return true;
		}
		String serviceId = serviceOrderDetail.getServiceId();
		ServiceOrders serviceOrder = this.getDao().get(serviceId);
		if (ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.getValue() == serviceOrder.getSrcType()) {
			return false;
		} else if (ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue() == serviceOrder.getSrcType()) {
			if (ORDER_STATUS_COMPLETE.equals(serviceOrder.getOrderSt())) {
				return true;
			} else {
				return false;
			}
		}

		return false;
	}

	@Transactional(readOnly = false)
	public ServiceOrders prepareReturnShipfareSO(Orders order, BigDecimal returnShipfare, String memo) {
		ServiceOrders serviceOrder = new ServiceOrders();
		BeanUtils.copyProperties(order, serviceOrder);
		serviceOrder.setReceiverEmail(order.getReceiverEmail());
		serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.SHIP_EXCEPTION.getValue());
		serviceOrder.setAccessaryDescription("无");
		serviceOrder.setDescription("退还溢收运费");
		serviceOrder.setReason("退还溢收运费");
		serviceOrder.setReasonTp(ServiceOrders.REASON_TYPE.USER_RETURN_SHIPFARE.getValue());
		Date createDate = new Date();
		serviceOrder.setCreateDate(createDate);
		serviceOrder.setCreateUser("SYSTEM");
		Member member = null;
		member = (Member) baseMgr.get(Member.class, order.getMemberId());
		serviceOrder.setLoginId(member.getLoginId());
		serviceOrder.setMemberId(member.getMemberId());
		serviceOrder.setMemberName(member.getName());
		serviceOrder.setMemo(memo);
		serviceOrder.setOrderSt(Orders.ORDER_ST.ORDER_CREATE.getValue());
		serviceOrder.setReturnSt("10");

		serviceOrder.setModifyUser("SYSTEM");
		serviceOrder.setServiceDt(createDate);
		serviceOrder.setOrderId(order.getOrderId());
		serviceOrder.setClientIp("localhost");

		Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();

		ServiceOrderProduct sop = new ServiceOrderProduct();
		BeanUtils.copyProperties(order.getOrderProduct().get(0), sop);
		sop.setOrderProdId(new Long(0));
		sop.setQty(0);
		sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());

		ServiceOrderDetail sod = new ServiceOrderDetail();
		BeanUtils.copyProperties(order.getOrderProduct().get(0).getOrderProductDetail().get(0), sod);
		sod.setOrderDetailId(new Long(0));
		sod.setOrgDetailSeq(sod.getDetailSeq());
		sod.setOrgProdSpecId(sod.getProdSpecId());
		sod.setOrgProdSpecName(sod.getProdSpecName());
		sod.setQty(0);
		sod.setGiftcardBalance(new BigDecimal(0));// 20110919 by feeling(giftcard)
		sop.getServiceOrderDetail().add(sod);

		serviceOrderProducts.add(sop);

		serviceOrder.setReturnBenefit(new BigDecimal(0));
		serviceOrder.setReturnBonus(new BigDecimal(0));
		serviceOrder.setReturnCoupon(new BigDecimal(0));
		serviceOrder.setReturnCash(new BigDecimal(0));
		serviceOrder.setReturnMyWallet(new BigDecimal(0));
		serviceOrder.setReturnShipfare(new BigDecimal(0));
		serviceOrder.setReturnCodFee(new BigDecimal(0));
		serviceOrder.setRealBenefit(new BigDecimal(0));
		serviceOrder.setRealBonus(new BigDecimal(0));
		serviceOrder.setRealCoupon(new BigDecimal(0));
		serviceOrder.setRealCash(new BigDecimal(0));
		serviceOrder.setRealMyWallet(new BigDecimal(0));
		serviceOrder.setRealShipfare(new BigDecimal(0));
		serviceOrder.setRealCodFee(new BigDecimal(0));
		serviceOrder.setReturnInstallmentFee(new BigDecimal(0));
		serviceOrder.setRealInstallmentFee(new BigDecimal(0));
		serviceOrder.setReturnMyWallet(returnShipfare);
		serviceOrder.setRealMyWallet(returnShipfare);
		serviceOrder.setReturnGiftcardBalance(new BigDecimal(0));// 20110919 by feeling(giftcard)
		serviceOrder.setRealGiftcardBalance(new BigDecimal(0));// 20110919 by feeling(giftcard)
		serviceOrder.setServiceOrderProducts(serviceOrderProducts);
		// 不同步到后台，由外部程式处理
		saveServiceOrders(order, serviceOrder, serviceOrderProducts, false);
		return serviceOrder;
	}

	// 未付款（取消订单退款/客服修改订单）
	public void refundForNotPaid(Orders order) {
		// 外部商城訂單不需作退款的動作
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			this.refundForNotPaid(order, true);
		}
	}

	public void refundForNotPaid(Orders order, boolean isMail) {
		Date currentDate = new Date();
		// 完成退款, 將金額退至virtual balance
		VirtualBalance vb = null;
		if (order.getMyWallet().add(order.getCash()).compareTo(BigDecimal.ZERO) == 1) {
			vb = new VirtualBalance();
			vb.setCreateDate(currentDate);
			vb.setDescription("订单取消退款(订单编号:" + "<a href=\"" + ServerValue.getFrontContextPath()
					+ "/member/order/page.do?page=detail&orderId=" + order.getOrderId() + "\">" + order.getOrderId() + "</a> )");
			vb.setMemberId(order.getMemberId());

			vb.setTrxAmount(order.getMyWallet().add(order.getCash()));
			vb.setTrxDate(currentDate);
			vb.setTrxType("EC_ORDER_MAIN");
			vb.setTrxTypeId(order.getOrderId());
			vb.setType(VirtualBalance.VirtualBalancePayType.VirtualBalance.getValue());
		}
		// 禮品卡的vb
		VirtualBalance vbGiftCard = null;
		if (order.getGiftcardBalance().intValue() > 0) {
			vbGiftCard = new VirtualBalance();
			vbGiftCard.setCreateDate(currentDate);

			vbGiftCard.setDescription("订单取消退款(订单编号:" + "<a href=\"" + ServerValue.getFrontContextPath()
					+ "/member/order/page.do?page=detail&orderId=" + order.getOrderId() + "\">" + order.getOrderId() + "</a> )");
			vbGiftCard.setMemberId(order.getMemberId());
			vbGiftCard.setTrxAmount(order.getGiftcardBalance());
			vbGiftCard.setTrxDate(currentDate);
			vbGiftCard.setTrxType("EC_ORDER_MAIN");
			vbGiftCard.setTrxTypeId(order.getOrderId());
			vbGiftCard.setType(VirtualBalance.VirtualBalancePayType.GiftcardBalance.getValue());
		}

		try {
			// 退至virtual balance, 金额需大于0
			if (null != vb) {
				memberService.saveOrUpdateVirtualBalance(vb);
			}
			// 退至禮品卡, 金额需大于0
			if (null != vbGiftCard) {
				memberService.saveOrUpdateVirtualBalance(vbGiftCard);
			}
			// 退至bonus
			transferService.refoundBonusTransfer(order.getOrderId(), order.getBonus());
			// 退oupon
			if (order.getCoupon().floatValue() > 0) {
				for (OrderProduct op : order.getOrderProduct()) {
					if (op.getCouponId() != null) {
						couponService.unuseCoupon(op.getOrderId(), op.getCouponId());
					}
				}
			}
			// 退款寄信
			if (isMail) {
				sendMailForRefund(order, null, null);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}

	}

	// 已付款（从后台返回） 訂單運費返還
	@Transactional(readOnly = false)
	public void refundForPaid(ServiceOrders so, ServiceOrderBo sob) {
		Orders order = (Orders) baseMgr.get(Orders.class, so.getOrderId());
		// 外单避掉退款动作
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			// 服务单未写入outerOrderId
			// if (StringUtils.isBlank(so.getOuterOrderId())) {
			// 完成退款, 將金額退至virtual balance
			VirtualBalance vb = new VirtualBalance();
			Date currentDate = new Date();
			vb.setCreateDate(currentDate);
			// 1.退货单 2.换货单 4.配送异常
			if (so.getSrcType() == 1 || so.getSrcType() == 2 || so.getSrcType() == 4) {
				vb.setApproveDate(so.getRefundDate());
			}
			DetachedCriteria dc = DetachedCriteria.forClass(ServiceOrderProduct.class);
			dc.add(Restrictions.eq("serviceId", so.getServiceId()));
			Object[] sops = baseMgr.findByCriteria(dc);
			ServiceOrderProduct sop = (ServiceOrderProduct) sops[0];
			// TODO
			vb.setDescription("订单" + (so.getSrcType() == 3 ? "取消" : "") + "退款(订单编号:" + "<a href=\""
					+ ServerValue.getFrontContextPath() + "/member/order/page.do?page=detail&orderId=" + so.getOrderId() + "\">"
					+ so.getOrderId() + "</a>)查看<a href=\"" + ServerValue.getFrontContextPath()
					+ "/member/serviceOrder/refund.do?orderProductId=" + sop.getOrderProdId() + "\">退款明细</a>");
			vb.setMemberId(so.getMemberId());
			if (so.getPaymentTp().equals("10") || so.getPaymentTp().equals("20") || so.getPaymentTp().equals("30")
					|| so.getPaymentTp().equals("40") || so.getPaymentTp().equals("60")) {
				vb.setTrxAmount(new BigDecimal(sob.getRealMyWallet() + sob.getRealCash()));
			} else {
				vb.setTrxAmount(new BigDecimal(sob.getRealMyWallet()));
			}
			vb.setTrxDate(currentDate);
			vb.setTrxType("EC_ORDER_MAIN");
			vb.setTrxTypeId(so.getOrderId());
			vb.setType(VirtualBalance.VirtualBalancePayType.VirtualBalance.getValue());

			// 禮品卡的vb
			VirtualBalance vbGiftCard = null;
			if (sob.getRealGiftcardBalance() != 0) {
				vbGiftCard = new VirtualBalance();
				vbGiftCard.setCreateDate(currentDate);
				// 1.退货单 2.换货单 4.配送异常
				if (so.getSrcType() == 1 || so.getSrcType() == 2 || so.getSrcType() == 4) {
					vbGiftCard.setApproveDate(so.getRefundDate());
				}
				String typeName = "";
				if (so.getSrcType() == 3) {
					typeName = "取消";
				} else if (so.getSrcType() == 1) {
					typeName = "退貨";
				}

				vbGiftCard.setDescription("订单" + typeName + "退款(订单编号:" + "<a href=\"" + ServerValue.getFrontContextPath()
						+ "/member/order/page.do?page=detail&orderId=" + so.getOrderId() + "\">" + so.getOrderId()
						+ "</a>)查看<a href=\"" + ServerValue.getFrontContextPath()
						+ "/member/serviceOrder/refund.do?orderProductId=" + sop.getOrderProdId() + "\">退款明细</a>");
				vbGiftCard.setMemberId(so.getMemberId());
				vbGiftCard.setTrxAmount(new BigDecimal(sob.getRealGiftcardBalance()));
				vbGiftCard.setTrxDate(currentDate);
				vbGiftCard.setTrxType("EC_ORDER_MAIN");
				vbGiftCard.setTrxTypeId(so.getOrderId());
				vbGiftCard.setType(VirtualBalance.VirtualBalancePayType.GiftcardBalance.getValue());
			}
			try {
				// 退至virtual balance, 金额需大于0
				if (vb.getTrxAmount().floatValue() > 0) {
					memberService.saveOrUpdateVirtualBalance(vb);
				}
				// 退至禮品卡, 金额需大于0
				if (null != vbGiftCard) {
					memberService.saveOrUpdateVirtualBalance(vbGiftCard);
				}
				// 退至bonus
				transferService.refoundBonusTransfer(so.getOrderId(), so.getRealBonus());
				// 退oupon
				if (so.getRealCoupon().floatValue() > 0) {
					for (Object obj : sops) {
						ServiceOrderProduct tsop = (ServiceOrderProduct) obj;
						if (tsop.getCouponId() != null) {
							couponService.unuseCoupon(tsop.getOrderId(), tsop.getCouponId());
						}
					}
				}

				if (so.getSrcType() == 1 || (so.getSrcType() == 2 && "30".equals(so.getReturnSt()))) {
					// 退货, 换转退追回积分
					if (null != order.getOrderBonusCampaign()
							&& order.getOrderBonusCampaign().getGiveBonus().compareTo(new BigDecimal(0)) == 1) {
						float orgAmount = order.getMustPay().subtract(order.getShipfare()).subtract(order.getCodFee()).add(
								order.getMyWallet()).add(order.getGiftcardBalance()).floatValue();
						float returnAmount = sob.getRealCash() + sob.getRealMyWallet() + sob.getRealGiftcardBalance();
						BigDecimal cancelBonus = new BigDecimal(Math.ceil((returnAmount / orgAmount)
								* (order.getOrderBonusCampaign().getGiveBonus().floatValue())));
						cancelBonus = memberService.cancelOrderBonus(cancelBonus, order.getOrderId(), order.getMemberId());
						if (null != cancelBonus) {
							OrderMemo orderMemo = new OrderMemo();
							orderMemo.setOrderId(order.getOrderId());
							orderMemo.setMemo(new Date() + " 退货完成, 追回" + cancelBonus.toString() + "积分");
							orderService.saveOrderMemo(orderMemo);
						}
					}
				}
				sendMailForRefund(order, so, sops);
			} catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
	}

	public void returnInventory(Orders order) {
		// 外部商城訂單不需退庫存
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			for (OrderProduct op : order.getOrderProduct()) {
				for (OrderProductDetail opd : op.getOrderProductDetail()) {

					String hql = "from SpecificationInventory where specId=?";

					Object[] specInventorys = baseMgr.queryByHql(hql, new Object[] { opd.getProdSpecId() });
					SpecificationInventory si = (SpecificationInventory) specInventorys[0];
					si.setCancelInventory(si.getCancelInventory() + opd.getQty());
					baseMgr.update(si);
					productService.checkIsSoldOut(opd.getProdId());
				}
			}
			if (order.getTuangouId() != null) {
				ShelfTuangou tuangou = (ShelfTuangou) baseMgr.get(ShelfTuangou.class, order.getTuangouId());
				if (null != tuangou) {
					tuangou.setCancelQty(tuangou.getCancelQty() + 1);
					baseMgr.save(tuangou);
				}
			}
		}
	}

	@Transactional(readOnly = false)
	public void saveOrderDetail(ServiceOrderDetail serviceOrderDetail) throws CoreException {
		serviceOrderDetailDao.saveOrUpdate(serviceOrderDetail);
	}

	@Transactional(readOnly = false)
	public void saveServiceOrderProd(ServiceOrderProduct serviceOrderProduct) throws CoreException {
		serviceOrderProductDao.saveOrUpdate(serviceOrderProduct);
	}

	@Transactional(readOnly = false)
	public ServiceOrders saveServiceOrders(Orders order, ServiceOrders serviceOrder, Collection serviceOrderProd,
			Boolean mustSyncToERP) throws CoreException {

		serviceOrder.setReceiverEmail(order.getReceiverEmail());
		if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
			// 判断call in 或 call out
			ServiceOrderProduct sop;
			serviceOrder.setCallInOrOut("out");
			for (Iterator<ServiceOrderProduct> iter = serviceOrderProd.iterator(); iter.hasNext();) {
				ServiceOrderProduct serviceOrderProdEntity = iter.next();
				sop = serviceOrderProdEntity;

				for (ServiceOrderDetail sod : sop.getServiceOrderDetail()) {
					if (sod.getDetailTp().equals("0")) {
						if (sod.getUnitPrice().floatValue() >= 500) {
							serviceOrder.setCallInOrOut("in");
							break;
						}
					}

				}
				if (sop.getProdTp().equals("6")) {
					// N选M
					if (sop.getUnitPrice().floatValue() >= 500) {
						serviceOrder.setCallInOrOut("in");
						break;
					}
				}

			}
		}
		// 達芙妮專用
		if ("30".equals(order.getReceiverTp())) {
			serviceOrder.setStoreId(order.getStoreId());
			serviceOrder.setReceiverTp(order.getReceiverTp());
		}
		String version = deliveryRuleService.findAddressVersion();
		serviceOrder.setVersion(version);

		if (serviceOrder.getServiceId() != null) {
			baseMgr.update(serviceOrder);
		} else {
			this.save(serviceOrder);
		}
		ServiceOrderProduct sop1 = null;
		for (Iterator<ServiceOrderProduct> iter = serviceOrderProd.iterator(); iter.hasNext();) {
			ServiceOrderProduct serviceOrderProdEntity = iter.next();
			sop1 = serviceOrderProdEntity;
			serviceOrderProdEntity.setServiceId(serviceOrder.getServiceId());

			serviceOrderProductDao.save(serviceOrderProdEntity);
			for (Iterator<ServiceOrderDetail> iterDetail = serviceOrderProdEntity.getServiceOrderDetail().iterator(); iterDetail
					.hasNext();) {
				ServiceOrderDetail serviceOrderDetailEntity = iterDetail.next();
				serviceOrderDetailEntity.setServiceId(serviceOrder.getServiceId());
				serviceOrderDetailEntity.setServiceProdId(serviceOrderProdEntity.getServiceProdId());
				serviceOrderDetailDao.save(serviceOrderDetailEntity);
			}
		}

		if (mustSyncToERP) {
			if (serviceOrder.getSrcType() != ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
				// 不为退货单，直接后送
				serviceOrder.setServiceOrderProducts(serviceOrderProd);
				if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.CANCEL.getValue()) {
					// 將n選m的order detail移掉與order product相同prodspecid的內容

					for (OrderProduct op : order.getOrderProduct()) {

						if (op.getProdTp().equals("6")) {
							List<OrderProductDetail> list = new ArrayList<OrderProductDetail>();
							// N選M
							for (OrderProductDetail opd : op.getOrderProductDetail()) {

								if (op.getProdSpecId().equals(opd.getProdSpecId())) {
									logger.debug("N pick M:avoid NM orderProduct's prodSpecId:" + op.getProdSpecId());
									// op.getOrderProductDetail().remove(opd);
									list.add(opd);
								}
							}
							for (OrderProductDetail opd : list) {
								op.getOrderProductDetail().remove(opd);
							}
						}
					}
					if (ServiceOrders.REASON_TYPE.CSR_UPDATEORDER.getValue().equals(serviceOrder.getReasonTp())) {
						// 客服修改订单的取消
						serviceOrder.setSrcType(8);
						batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(serviceOrder)
								+ "</root>", null, serviceOrder.getServiceId());
					} else {
						// 取消单要给order
						batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(serviceOrder)
								+ toXML(order) + "</root>", null, serviceOrder.getServiceId());
						Orders suckOrder = (Orders) baseMgr.get(Orders.class, order.getOrderId());
						suckOrder.setSync(true);
						baseMgr.update(suckOrder);
					}
				} else if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.CHANGE.getValue()) {
					// 换货单
					sendMail(order, serviceOrder, serviceOrderProd.toArray());
					batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(serviceOrder)
							+ "</root>", null, serviceOrder.getServiceId());
				} else {
					// 出货异常
					batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(serviceOrder)
							+ "</root>", null, serviceOrder.getServiceId());
				}
			} else {
				Question question = new Question();
				DetachedCriteria dc = DetachedCriteria.forClass(FromChannel.class);
				dc.add(Restrictions.eq("title", "WEB form"));
				Object[] channels = baseMgr.findByCriteria(dc);
				if (channels != null) {
					question.setChannel((FromChannel) channels[0]);
				}
				question.setCreateDate(new Date());
				question.setCreateUser("SYSTEM");
				question.setIsMember(true);
				question.setMemberId(serviceOrder.getMemberId());
				question.setModifyUser("SYSTEM");
				String questionTypeName = "ReturnProductApply";
				QuestionType type = (QuestionType) baseMgr.get(QuestionType.class, questionTypeName);
				question.setType(type);
				for (Attribute attribute : type.getExtraAttr().getAttributes()) {
					QuestionExtraValue ea = new QuestionExtraValue();
					ea.setQuestion(question);
					ea.setName(attribute.getName());
					ea.setAttribute(attribute);
					if (ea.getName().equals("productId")) {
						ea.setValue(sop1.getProdId());
					} else if (ea.getName().equals("orderId")) {
						ea.setValue(sop1.getOrderId());
					} else if (ea.getName().equals("returnId")) {
						ea.setValue(serviceOrder.getServiceId());
					}
					baseMgr.save(ea);
				}
				baseMgr.save(question);
				// 退貨單狀態為00的時候，表示是從前台退，這時才要成立工單
				if ("00".equals(serviceOrder.getOrderSt())) {
					workOrderService.createWorkOrder("SYSTEM", question.getId(), question.getShortDesc(), false, null);
					sendMail(order, serviceOrder, serviceOrderProd.toArray());
				} else if ("10".equals(serviceOrder.getOrderSt())) {
					sendMailForReturnProductConfirm(order, serviceOrder, serviceOrderProd.toArray());
				}
			}
		} else {
			Orders suckOrder = (Orders) baseMgr.get(Orders.class, order.getOrderId());
			suckOrder.setSync(true);
			baseMgr.update(suckOrder);
		}
		return serviceOrder;
	}

	@Transactional(readOnly = false)
	public void sendMail(Orders order, ServiceOrders serviceOrder, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(serviceOrder.getOuterOrderId())) {
			// 退換貨
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
			Map<String, Object> objs = new HashMap<String, Object>();
			objs.put("basehref", "http://" + order.getDomainSrc());
			objs.put("contextPath", ServerValue.getFrontContextPath());
			objs.put("order", order);
			String mailTemplate = "";
			objs.put("myWallet", JspUtil.currency(serviceOrder.getReturnMyWallet()));
			objs.put("thirdParty", JspUtil.currency(serviceOrder.getReturnCash()));
			objs.put("bonus", JspUtil.currency(serviceOrder.getReturnBonus()));
			objs.put("receiverName", serviceOrder.getReceiverName());
			objs
					.put("receiverAddr", serviceOrder.getReceiverProvince() + "，" + serviceOrder.getReceiverCity() + "，"
							+ serviceOrder.getReceiverArea() + "，" + serviceOrder.getReceiverAddr() + "，"
							+ serviceOrder.getReceiverZip());
			objs.put("receiverMobile", serviceOrder.getReceiverMobile());
			if (serviceOrder.getReceiverPhone() != null && serviceOrder.getReceiverPhone().length() > 1) {
				String receiverPhone = "";
				receiverPhone += serviceOrder.getReceiverPhoneArea() != null ? serviceOrder.getReceiverPhoneArea() + "-" : "";
				receiverPhone += serviceOrder.getReceiverPhone() != null ? serviceOrder.getReceiverPhone() : "";
				receiverPhone += serviceOrder.getReceiverExt() != null ? "分机：" + serviceOrder.getReceiverExt() : "";
				objs.put("receiverPhone", receiverPhone);
			} else {
				objs.put("receiverPhone", " ");
			}
			// System.out.println("callInOrOut:"+serviceOrder.getCallInOrOut());
			// System.out.println(serviceOrder.getSrcType());
			Member member = (Member) baseMgr.get(Member.class, serviceOrder.getMemberId());
			if (serviceOrder.getSrcType() == 1) {
				if (sops != null && sops.length > 0) {
					Object obj = sops[0];
					Calendar calendar = Calendar.getInstance();
					Date logisticFinalDt = ((ServiceOrderProduct) obj).getLogisticFinalDt();
					if (logisticFinalDt != null) {
						calendar.setTime(logisticFinalDt);
					}
					calendar.add(Calendar.DAY_OF_MONTH, 7);
					objs.put("mustCallDate", sdf2.format(calendar.getTime()));

					objs.put("applyDate", sdf.format(serviceOrder.getCreateDate()));

				}
				// mailTemplate = "return_product_notice";

				if ("out".equals(serviceOrder.getCallInOrOut())) {
					mailTemplate = "return_product_need_approve";

				} else {

					mailTemplate = "return_product_call_in";
				}
				if (member.getMobile() != null && member.getMobile().length() > 0) {
					String msg = "";
					// send sms
					if ("out".equals(serviceOrder.getCallInOrOut())) {
						// 审核
						msg = "您好，您已提交订单" + order.getOrderId() + "的退货申请，耀点100将于24小时内进行审核，并以短信与电邮通知您审核结果";

					} else {
						// call in
						msg = "您好，您已提交订单" + order.getOrderId() + "的退货申请，请于签收商品7日内致电耀点100客服400-1168-100，以便和您确认退货信息。";
					}
					smsService.sendSms(member.getMobile(), msg, order.getOrderId());
				}
			} else if (serviceOrder.getSrcType() == 2) {
				if (sops != null && sops.length > 0) {
					Object obj = sops[0];
					Calendar calendar = Calendar.getInstance();
					Date logisticFinalDt = ((ServiceOrderProduct) obj).getLogisticFinalDt();
					if (logisticFinalDt != null) {
						calendar.setTime(logisticFinalDt);
					}
					calendar.add(Calendar.DAY_OF_MONTH, 15);
					objs.put("mustSendDate", sdf.format(calendar.getTime()));
				}
				mailTemplate = "change_product_notice";
			}
			// System.out.println("mailTemplate:"+mailTemplate);
			objs.put("reason", serviceOrder.getReason());
			objs.put("description", serviceOrder.getDescription());
			objs.put("accessaryDescription", serviceOrder.getAccessaryDescription());
			objs.put("orderId", order.getOrderId());
			objs.put("orderDate", sdf.format(order.getCreateDate()));
			Option option = JspUtil.menuOption("Order.PaymentTp", order.getPaymentTp());
			String optionName = "";
			if (option != null) {
				optionName = option.getName();
			}
			objs.put("paymentType", optionName);
			objs.put("memberName", order.getMemberName());
			objs.put("serviceOrder", serviceOrder);
			objs.put("serviceId", serviceOrder.getServiceId());
			objs.put("sop", sops[0]);
			objs.put("sops", sops);
			objs.put("domainSrc", order.getDomainSrc());
			Mail entity = velocityService.formatToMail(mailTemplate, objs);
			// System.out.println(entity.getBody());

			entity.addTo(member.getLoginId(), member.getEmail());
			entity.setFrom(envParameters.getEmailFromCs());
			mailService.save(entity);
		}
	}

	@Transactional(readOnly = false)
	public void sendMail(String velocityTemplate, String mailSubject, Orders order) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			Map<String, Object> contentMap = new HashMap<String, Object>();
			contentMap.put("order", order);
			CommonCriteria crit = new CommonCriteria();
			crit.addEq("orderId", order.getOrderId());
			List<OrderProductDetail> getOrderProdDetailList = new ArrayList();
			for (OrderProduct op : order.getOrderProduct()) {
				getOrderProdDetailList.addAll(op.getOrderProductDetail());
			}
			contentMap.put("orderdetails", getOrderProdDetailList);
			contentMap.put("paymentTpMenu", menuService.get("Order.PaymentTp"));
			contentMap.put("contextpath", ServerValue.getFrontContextPath());
			contentMap.put("basehref", "http://" + order.getDomainSrc());
			contentMap.put("cancelTime", df.format(new Date()));
			Mail mail = velocityService.formatToMail(velocityTemplate, contentMap);
			mail.setFrom(envParameters.getEmailFromSystem());
			mail.setTo(order.getReceiverEmail());
			mail.setSubject(mailSubject);
			mailService.save(mail);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForChangeDone(Orders order, ServiceOrders serviceOrder, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(serviceOrder.getOuterOrderId())) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Map<String, Object> objs = new HashMap<String, Object>();
			objs.put("basehref", "http://" + order.getDomainSrc());
			objs.put("contextPath", ServerValue.getFrontContextPath());
			objs.put("order", order);
			String mailTemplate = "";
			objs.put("myWallet", JspUtil.currency(serviceOrder.getReturnMyWallet()));
			objs.put("thirdParty", JspUtil.currency(serviceOrder.getReturnCash()));
			objs.put("bonus", JspUtil.currency(serviceOrder.getReturnBonus()));
			objs.put("receiverName", serviceOrder.getReceiverName());
			objs.put("receiverAddr", serviceOrder.getReceiverProvince() + serviceOrder.getReceiverCity()
					+ serviceOrder.getReceiverArea() + serviceOrder.getReceiverAddr() + serviceOrder.getReceiverZip());
			objs.put("receiverMobile", serviceOrder.getReceiverMobile());
			if (serviceOrder.getReceiverPhone() != null && serviceOrder.getReceiverPhone().length() > 1) {
				String receiverPhone = "";
				receiverPhone += serviceOrder.getReceiverPhoneArea() != null ? serviceOrder.getReceiverPhoneArea() + "-" : "";
				receiverPhone += serviceOrder.getReceiverPhone() != null ? serviceOrder.getReceiverPhone() : "";
				receiverPhone += serviceOrder.getReceiverExt() != null ? "分机：" + serviceOrder.getReceiverExt() : "";
				objs.put("receiverPhone", receiverPhone);
			} else {
				objs.put("receiverPhone", " ");
			}

			if (serviceOrder.getSrcType() == 1) {
				if (sops != null && sops.length > 0) {
					Object obj = sops[0];
					Calendar calendar = Calendar.getInstance();
					Date logisticFinalDt = ((ServiceOrderProduct) obj).getLogisticFinalDt();
					if (logisticFinalDt != null) {
						calendar.setTime(logisticFinalDt);
					}
					calendar.add(Calendar.DAY_OF_MONTH, 7);
					objs.put("mustSendDate", sdf.format(calendar.getTime()));
				}
				mailTemplate = "return_product_notice";
			} else if (serviceOrder.getSrcType() == 2) {
				if (sops != null && sops.length > 0) {
					Object obj = sops[0];
					Calendar calendar = Calendar.getInstance();
					Date logisticFinalDt = ((ServiceOrderProduct) obj).getLogisticFinalDt();
					if (logisticFinalDt != null) {
						calendar.setTime(logisticFinalDt);
					}
					calendar.add(Calendar.DAY_OF_MONTH, 15);
					objs.put("mustSendDate", sdf.format(calendar.getTime()));
				}
				mailTemplate = "change_product_notice";
			}
			objs.put("reason", serviceOrder.getReason());
			objs.put("description", serviceOrder.getDescription());
			objs.put("accessaryDescription", serviceOrder.getAccessaryDescription());
			objs.put("orderId", order.getOrderId());
			objs.put("orderDate", sdf.format(order.getCreateDate()));
			objs.put("paymentType", JspUtil.menuOption("Order.PaymentTp", order.getPaymentTp()).getName());
			objs.put("memberName", order.getMemberName());
			objs.put("serviceOrder", serviceOrder);
			objs.put("serviceId", serviceOrder.getServiceId());
			objs.put("sop", sops[0]);
			objs.put("sops", sops);
			objs.put("domainSrc", order.getDomainSrc());
			Mail entity = velocityService.formatToMail(mailTemplate, objs);
			// System.out.println(entity.getBody());
			Member member = (Member) baseMgr.get(Member.class, serviceOrder.getMemberId());
			entity.addTo(member.getLoginId(), member.getEmail());
			entity.setFrom(envParameters.getEmailFromCs());
			mailService.save(entity);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForChangeFail(Orders order, ServiceOrders serviceOrder, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(serviceOrder.getOuterOrderId())) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Map<String, Object> objs = new HashMap<String, Object>();
			objs.put("basehref", "http://" + order.getDomainSrc());
			objs.put("contextPath", ServerValue.getFrontContextPath());
			objs.put("returnProductDate", sdf.format(serviceOrder.getModifyDate()));
			objs.put("order", order);
			String mailTemplate = "";
			objs.put("myWallet", JspUtil.currency(serviceOrder.getReturnMyWallet()));
			objs.put("thirdParty", JspUtil.currency(serviceOrder.getReturnCash()));
			objs.put("bonus", JspUtil.currency(serviceOrder.getReturnBonus()));
			objs.put("receiverName", serviceOrder.getReceiverName());
			objs.put("receiverAddr", serviceOrder.getReceiverProvince() + serviceOrder.getReceiverCity()
					+ serviceOrder.getReceiverArea() + serviceOrder.getReceiverAddr() + serviceOrder.getReceiverZip());
			objs.put("receiverMobile", serviceOrder.getReceiverMobile());
			if (serviceOrder.getReceiverPhone() != null && serviceOrder.getReceiverPhone().length() > 1) {
				String receiverPhone = "";
				receiverPhone += serviceOrder.getReceiverPhoneArea() != null ? serviceOrder.getReceiverPhoneArea() + "-" : "";
				receiverPhone += serviceOrder.getReceiverPhone() != null ? serviceOrder.getReceiverPhone() : "";
				receiverPhone += serviceOrder.getReceiverExt() != null ? "分机：" + serviceOrder.getReceiverExt() : "";
				objs.put("receiverPhone", receiverPhone);
			} else {
				objs.put("receiverPhone", " ");
			}

			if (sops != null && sops.length > 0) {
				Object obj = sops[0];
				Calendar calendar = Calendar.getInstance();
				Date logisticFinalDt = ((ServiceOrderProduct) obj).getLogisticFinalDt();
				if (logisticFinalDt != null) {
					calendar.setTime(logisticFinalDt);
				}
				calendar.add(Calendar.DAY_OF_MONTH, 15);
				objs.put("mustSendDate", sdf.format(calendar.getTime()));
			}
			mailTemplate = "change_product_fail";

			objs.put("reason", serviceOrder.getReason());
			objs.put("description", serviceOrder.getDescription());
			objs.put("accessaryDescription", serviceOrder.getAccessaryDescription());
			objs.put("orderId", order.getOrderId());
			objs.put("orderDate", sdf.format(order.getCreateDate()));
			String optionName = " ";
			try {
				Option option = JspUtil.menuOption("Order.PaymentTp", order.getPaymentTp());
				if (option != null) {
					optionName = option.getName();
				}
			} catch (Exception e) {

			}

			objs.put("paymentType", optionName);
			objs.put("memberName", order.getMemberName());
			objs.put("serviceOrder", serviceOrder);
			objs.put("serviceId", serviceOrder.getServiceId());
			objs.put("sop", sops[0]);
			objs.put("sops", sops);
			objs.put("domainSrc", order.getDomainSrc());
			Mail entity = velocityService.formatToMail(mailTemplate, objs);
			// System.out.println(entity.getBody());
			Member member = (Member) baseMgr.get(Member.class, serviceOrder.getMemberId());
			entity.addTo(member.getLoginId(), member.getEmail());
			entity.setFrom(envParameters.getEmailFromCs());
			mailService.save(entity);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForChangeNote(Orders order, ServiceOrders so, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(so.getOuterOrderId())) {
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			Map<String, Object> contentMap = new HashMap<String, Object>();
			contentMap.put("order", order);
			contentMap.put("memberName", order.getMemberName());
			contentMap.put("orderId", order.getOrderId());
			// contentMap.put("sendDate", df.format(so.getModifyDate()));
			contentMap.put("sendDate", df.format(new Date()));
			contentMap.put("serviceId", so.getServiceId());
			contentMap.put("receiverName", order.getReceiverName() != null ? order.getReceiverName() : " ");
			contentMap.put("receiverAddr", order.getReceiverProvince() + "，" + order.getReceiverCity() + "，"
					+ order.getReceiverArea() + "，" + order.getReceiverAddr() + "，" + order.getReceiverZip());
			contentMap.put("deliverType", "送货上門");
			Long orderProductId = null;
			String logisticNum = "";
			String logisticComp = "";
			if (sops != null) {
				for (Object obj : sops) {
					ServiceOrderProduct sop = (ServiceOrderProduct) obj;
					orderProductId = sop.getOrderProdId();
					logisticNum += "，" + sop.getLogisticRefNum();

					LogisticProvider lp = (LogisticProvider) baseMgr.get(LogisticProvider.class, sop.getLogisticCompany());
					if (null != lp) {
						logisticComp += "，" + lp.getName();
					} else {
						logisticComp += "，" + sop.getLogisticCompany();
					}
				}
			}
			if (logisticNum.length() > 2) {
				logisticNum = logisticNum.substring(1);
			}
			if (logisticComp.length() > 2) {
				logisticComp = logisticComp.substring(1);
			}
			contentMap.put("logisticNum", logisticNum);
			contentMap.put("logisticComp", logisticComp);
			contentMap.put("applyDate", df.format(so.getCreateDate()));
			contentMap.put("orderProductId", orderProductId);
			contentMap.put("sops", sops);

			contentMap.put("contextPath", ServerValue.getFrontContextPath());
			contentMap.put("basehref", "http://" + order.getDomainSrc());
			contentMap.put("domainSrc", order.getDomainSrc());
			Mail mail = velocityService.formatToMail("change_product_note", contentMap);
			// System.out.println(mail.getBody());
			mail.setFrom(envParameters.getEmailFromSystem());
			mail.setTo(order.getReceiverEmail());

			mailService.save(mail);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForRefund(Orders order, ServiceOrders so, Object[] sops) {
		// 外部商城訂單不寄信
		if ((null != so && StringUtils.isBlank(so.getOuterOrderId()))
				|| (null != order && StringUtils.isBlank(order.getOuterOrderId()))) {
			// 若沒有退任何錢，則不寄退款完成信

			if (so != null) {
				if ((so.getRealBenefit() == null || so.getRealBenefit().floatValue() == 0)
						&& (so.getRealBonus() == null || so.getRealBonus().floatValue() == 0)
						&& (so.getRealCash() == null || so.getRealCash().floatValue() == 0)
						&& (so.getRealMyWallet() == null || so.getRealMyWallet().floatValue() == 0)
						&& (so.getRealCoupon() == null || so.getRealCoupon().floatValue() == 0)
						&& (so.getRealGiftcardBalance() == null || so.getRealGiftcardBalance().floatValue() == 0)) {
					return;
				}
			}
			if (order != null) {
				if ((order.getBenefit() == null || order.getBenefit().floatValue() == 0)
						&& (order.getBonus() == null || order.getBonus().floatValue() == 0)
						&& (order.getCash() == null || order.getCash().floatValue() == 0)
						&& (order.getMyWallet() == null || order.getMyWallet().floatValue() == 0)
						&& (order.getCoupon() == null || order.getCoupon().floatValue() == 0)
						&& (order.getGiftcardBalance() == null || order.getGiftcardBalance().floatValue() == 0)) {
					return;
				}

			}
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			Map<String, Object> contentMap = new HashMap<String, Object>();
			contentMap.put("order", order);
			contentMap.put("orderId", order.getOrderId());
			contentMap.put("memberName", order.getMemberName());
			Long orderProductId = null;
			// Object[] sops=baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new
			// Object[]{so.getServiceId()});
			if (sops != null) {
				for (Object obj : sops) {
					ServiceOrderProduct sop = (ServiceOrderProduct) obj;
					orderProductId = sop.getOrderProdId();
				}
			} else {
				for (Object obj : order.getOrderProduct()) {
					OrderProduct op = (OrderProduct) obj;
					orderProductId = op.getOrderProdId();
				}
			}

			String optionName = " ";

			try {
				if (StringUtils.isNotBlank(order.getPaymentSrc())) {
					Option option = JspUtil.menuOption("Payment.Src", order.getPaymentSrc());
					if (option != null) {
						optionName = option.getName();
					}
				}
			} catch (Exception e) {

			}

			contentMap.put("thirdParty", optionName);
			String thirdPartyName = "";
			if (("50".equals(order.getPaymentTp()) || "70".equals(order.getPaymentTp()) || "80".equals(order.getPaymentTp()))
					&& "10".equals(order.getPaymentSt())) {
				String thirdPartySrc = ((ThirdPartyTransfer) baseMgr.get(ThirdPartyTransfer.class, order.getPaymentId()))
						.getSrc();
				if ("01".equals(thirdPartySrc)) {
					thirdPartyName = "退回银联帐户";
				} else if ("02".equals(thirdPartySrc)) {
					thirdPartyName = "退回财付通帐户";
				} else if ("03".equals(thirdPartySrc)) {
					thirdPartyName = "退回交易原支付银行卡/快钱帐户";
				} else if ("04".equals(thirdPartySrc)) {
					thirdPartyName = "退回交易原支付银行卡/支付宝帐户";
				} else if ("06".equals(thirdPartySrc)) {
					thirdPartyName = "退回交易原支付信用卡/快钱帐户";
				}
				contentMap.put("thirdPartyPayment", "Y");

			} else {
				contentMap.put("thirdPartyPayment", "N");
			}
			contentMap.put("thirdPartyName", thirdPartyName);

			contentMap.put("so", so);
			if (so != null) {
				contentMap.put("serviceId", so.getServiceId());
				contentMap.put("applyDate", df.format(so.getCreateDate()));

			} else {
				contentMap.put("serviceId", " ");
				contentMap.put("applyDate", " ");

			}
			contentMap.put("jspUtil", new JspUtil());

			contentMap.put("orderProductId", orderProductId);
			contentMap.put("sops", sops);
			if (so != null) {

				Float benefit = so.getRealBenefit().floatValue();
				Float bonus = so.getRealBonus().floatValue();
				Float cash = so.getRealCash().floatValue();
				Float myWallet = so.getRealMyWallet().floatValue();
				Float coupon = so.getRealCoupon().floatValue();
				Float installmentFee = so.getRealInstallmentFee().floatValue();
				Float giftcard = so.getRealGiftcardBalance().floatValue();

				contentMap.put("returnAmount", JspUtil.currency(benefit + bonus + coupon + cash + myWallet + installmentFee
						+ giftcard));
				contentMap.put("returnMyWallet", JspUtil.currency(so.getRealMyWallet()));
				contentMap.put("returnCash", JspUtil.currency(so.getRealCash().add(so.getRealInstallmentFee())));// vm就不用改相關的價錢欄位了
				contentMap.put("returnBonus", JspUtil.currency(so.getRealBonus()));
				if (so.getRealBonus().compareTo(BigDecimal.ZERO) == 1) {
					contentMap.put("returnOriginalBonus", so.getRealBonus().multiply(
							order.getOrderBonusCampaign().getOffsetRate()));
				} else {
					contentMap.put("returnOriginalBonus", 0);
				}
				contentMap.put("returnCoupon", JspUtil.currency(so.getRealCoupon()));
				contentMap.put("returnInstallFee", JspUtil.currency(so.getRealInstallmentFee()));
				contentMap.put("returnGiftcard", JspUtil.currency(so.getRealGiftcardBalance()));
				contentMap.put("realinReturn", JspUtil.currency(benefit + cash + myWallet + installmentFee));
				contentMap.put("notThirdParty", JspUtil.currency(cash + myWallet));
			} else {
				Float benefit = order.getBenefit().floatValue();
				Float bonus = order.getBonus().floatValue();
				Float cash = order.getCash().floatValue();
				Float myWallet = order.getMyWallet().floatValue();
				Float coupon = order.getCoupon().floatValue();
				Float installmentFee = order.getInstallmentFee().floatValue();
				Float giftcard = order.getGiftcardBalance().floatValue();
				contentMap.put("returnAmount", JspUtil.currency(benefit + bonus + coupon + cash + myWallet + installmentFee
						+ giftcard));
				contentMap.put("returnMyWallet", JspUtil.currency(myWallet));
				contentMap.put("returnCash", JspUtil.currency(cash + installmentFee));// vm就不用改相關的價錢欄位了
				contentMap.put("returnBonus", JspUtil.currency(bonus));
				if (order.getBonus().compareTo(BigDecimal.ZERO) == 1) {
					contentMap.put("returnOriginalBonus", order.getBonus()
							.multiply(order.getOrderBonusCampaign().getOffsetRate()));
				} else {
					contentMap.put("returnOriginalBonus", 0);
				}
				contentMap.put("returnCoupon", JspUtil.currency(coupon));
				contentMap.put("returnInstallFee", JspUtil.currency(installmentFee));
				contentMap.put("returnGiftcard", JspUtil.currency(giftcard));
				contentMap.put("realinReturn", JspUtil.currency(benefit + cash + myWallet + installmentFee));
				contentMap.put("notThirdParty", JspUtil.currency(cash + myWallet));
			}
			contentMap.put("contextPath", ServerValue.getFrontContextPath());
			contentMap.put("basehref", "http://" + order.getDomainSrc());
			contentMap.put("domainSrc", order.getDomainSrc());
			Mail mail = velocityService.formatToMail("refund", contentMap);
			// System.out.println(mail.getBody());
			mail.setFrom(envParameters.getEmailFromSystem());
			mail.setTo(order.getReceiverEmail());

			mailService.save(mail);
		}

	}

	@Transactional(readOnly = false)
	public void sendMailForReturnDone(Orders order, ServiceOrders so, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			Map<String, Object> contentMap = new HashMap<String, Object>();
			contentMap.put("order", order);
			contentMap.put("orderId", order.getOrderId());
			contentMap.put("memberName", order.getMemberName());
			Long orderProductId = null;
			// Object[] sops=baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new
			// Object[]{so.getServiceId()});
			for (Object obj : sops) {
				ServiceOrderProduct sop = (ServiceOrderProduct) obj;
				orderProductId = sop.getOrderProdId();
			}

			String optionName = " ";
			try {
				Option option = JspUtil.menuOption("payment.Src", order.getPaymentSrc());
				if (option != null) {
					optionName = option.getName();
				}
			} catch (Exception e) {

			}
			contentMap.put("thirdParty", optionName);

			contentMap.put("so", so);
			contentMap.put("serviceId", so.getServiceId());
			contentMap.put("number", new NumberTool());
			contentMap.put("aLocale", Locale.CHINA);
			contentMap.put("applyDate", df.format(so.getCreateDate()));
			contentMap.put("orderProductId", orderProductId);
			contentMap.put("sops", sops);
			contentMap.put("returnAmount", JspUtil.currency(so.getRealBenefit().add(so.getRealBonus())
					.add(so.getRealCoupon()).add(so.getRealCash()).add(so.getRealMyWallet()).add(so.getRealShipfare())));
			contentMap.put("returnMyWallet", JspUtil.currency(so.getRealMyWallet()));
			contentMap.put("returnCash", JspUtil.currency(so.getRealCash()));
			contentMap.put("returnBonus", JspUtil.currency(so.getRealBonus()));
			contentMap.put("contextPath", ServerValue.getFrontContextPath());
			contentMap.put("basehref", "http://" + order.getDomainSrc());

			Mail mail = velocityService.formatToMail("return_product_done", contentMap);
			// System.out.println(mail.getBody());
			mail.setFrom(envParameters.getEmailFromSystem());
			mail.setTo(order.getReceiverEmail());

			mailService.save(mail);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForReturnFail(Orders order, ServiceOrders serviceOrder, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			SimpleDateFormat sdfShort = new SimpleDateFormat("yyyy-MM-dd");
			Map<String, Object> objs = new HashMap<String, Object>();
			objs.put("basehref", "http://" + order.getDomainSrc());
			objs.put("contextPath", ServerValue.getFrontContextPath());
			objs.put("returnProductDate", sdfShort.format(order.getModifyDate()));
			objs.put("applyDate", sdf.format(serviceOrder.getCreateDate()));
			objs.put("order", order);
			String mailTemplate = "";
			objs.put("myWallet", JspUtil.currency(serviceOrder.getReturnMyWallet()));
			objs.put("thirdParty", JspUtil.currency(serviceOrder.getReturnCash()));
			objs.put("bonus", JspUtil.currency(serviceOrder.getReturnBonus()));
			objs.put("receiverName", serviceOrder.getReceiverName());
			objs.put("receiverAddr", serviceOrder.getReceiverProvince() + serviceOrder.getReceiverCity()
					+ serviceOrder.getReceiverArea() + serviceOrder.getReceiverAddr() + serviceOrder.getReceiverZip());
			objs.put("receiverMobile", serviceOrder.getReceiverMobile());
			if (serviceOrder.getReceiverPhone() != null && serviceOrder.getReceiverPhone().length() > 1) {
				String receiverPhone = "";
				receiverPhone += serviceOrder.getReceiverPhoneArea() != null ? serviceOrder.getReceiverPhoneArea() + "-" : "";
				receiverPhone += serviceOrder.getReceiverPhone() != null ? serviceOrder.getReceiverPhone() : "";
				receiverPhone += serviceOrder.getReceiverExt() != null ? "分机：" + serviceOrder.getReceiverExt() : "";
				objs.put("receiverPhone", receiverPhone);
			} else {
				objs.put("receiverPhone", " ");
			}

			if (sops != null && sops.length > 0) {
				Object obj = sops[0];
				Calendar calendar = Calendar.getInstance();
				Date logisticFinalDt = ((ServiceOrderProduct) obj).getLogisticFinalDt();
				if (logisticFinalDt != null) {
					calendar.setTime(logisticFinalDt);
				}
				calendar.add(Calendar.DAY_OF_MONTH, 7);
				objs.put("mustSendDate", sdf.format(calendar.getTime()));
			}
			mailTemplate = "return_product_fail";

			objs.put("reason", serviceOrder.getReason());
			objs.put("description", serviceOrder.getDescription());
			objs.put("accessaryDescription", serviceOrder.getAccessaryDescription());
			objs.put("orderId", order.getOrderId());
			objs.put("orderDate", sdf.format(order.getCreateDate()));
			String optionName = "";

			try {
				Option option = JspUtil.menuOption("Order.PaymentTp", order.getPaymentTp());

				if (option != null) {
					optionName = option.getName();
				}
			} catch (Exception e) {

			}
			objs.put("paymentType", optionName);
			objs.put("memberName", order.getMemberName());
			objs.put("serviceOrder", serviceOrder);
			objs.put("serviceId", serviceOrder.getServiceId());
			objs.put("sop", sops[0]);
			objs.put("sops", sops);
			objs.put("domainSrc", order.getDomainSrc());
			Mail entity = velocityService.formatToMail(mailTemplate, objs);
			// System.out.println(entity.getBody());
			Member member = (Member) baseMgr.get(Member.class, serviceOrder.getMemberId());
			entity.addTo(member.getLoginId(), member.getEmail());
			entity.setFrom(envParameters.getEmailFromCs());
			mailService.save(entity);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForReturnNote(Orders order, ServiceOrders so, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
			Map<String, Object> contentMap = new HashMap<String, Object>();
			contentMap.put("order", order);
			contentMap.put("orderId", order.getOrderId());
			contentMap.put("memberName", order.getMemberName());
			Long orderProductId = null;
			for (Object obj : sops) {
				ServiceOrderProduct sop = (ServiceOrderProduct) obj;
				orderProductId = sop.getOrderProdId();
			}
			contentMap.put("applyDate", df.format(so.getCreateDate()));
			contentMap.put("orderProductId", orderProductId);
			contentMap.put("sops", sops);
			contentMap.put("serviceId", so.getServiceId());
			contentMap.put("contextPath", ServerValue.getFrontContextPath());
			contentMap.put("basehref", "http://" + order.getDomainSrc());
			contentMap.put("domainSrc", order.getDomainSrc());
			Mail mail = velocityService.formatToMail("return_product_note", contentMap);
			// System.out.println(mail.getBody());
			mail.setFrom(envParameters.getEmailFromSystem());
			mail.setTo(order.getReceiverEmail());

			mailService.save(mail);
		}
	}

	@Transactional(readOnly = false)
	public void sendMailForReturnProductConfirm(Orders order, ServiceOrders serviceOrder, Object[] sops) {
		// 外部商城訂單不需寄mail
		if (StringUtils.isBlank(order.getOuterOrderId())) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
			Map<String, Object> objs = new HashMap<String, Object>();
			objs.put("contextPath", ServerValue.getFrontContextPath());
			if (serviceOrder.getConfirmDt() != null) {
				objs.put("mustSendDate", sdf2.format(JspUtil.addDays(serviceOrder.getConfirmDt(), 3)));
			} else {
				objs.put("mustSendDate", " ");
			}
			objs.put("order", order);
			objs.put("basehref", "http://" + order.getDomainSrc());
			String mailTemplate = "";
			objs.put("myWallet", JspUtil.currency(serviceOrder.getRealMyWallet()));
			objs.put("thirdParty", JspUtil.currency(serviceOrder.getRealCash()));
			objs.put("bonus", JspUtil.currency(serviceOrder.getRealBonus()));
			objs.put("giftcard", JspUtil.currency(serviceOrder.getRealGiftcardBalance()));
			objs.put("notThirdParty", JspUtil.currency(serviceOrder.getRealMyWallet().add(serviceOrder.getRealCash())));

			objs.put("receiverName", serviceOrder.getReceiverName());
			objs.put("receiverAddr", serviceOrder.getReceiverProvince() + serviceOrder.getReceiverCity()
					+ serviceOrder.getReceiverArea() + serviceOrder.getReceiverAddr() + serviceOrder.getReceiverZip());
			objs.put("receiverMobile", serviceOrder.getReceiverMobile());
			if (serviceOrder.getReceiverPhone() != null && serviceOrder.getReceiverPhone().length() > 1) {
				String receiverPhone = "";
				receiverPhone += serviceOrder.getReceiverPhoneArea() != null ? serviceOrder.getReceiverPhoneArea() + "-" : "";
				receiverPhone += serviceOrder.getReceiverPhone() != null ? serviceOrder.getReceiverPhone() : "";
				receiverPhone += serviceOrder.getReceiverExt() != null ? "分机：" + serviceOrder.getReceiverExt() : "";
				objs.put("receiverPhone", receiverPhone);
			} else {
				objs.put("receiverPhone", " ");
			}
			String thirdPartyName = "";
			if (("50".equals(order.getPaymentTp()) || "70".equals(order.getPaymentTp()) || "80".equals(order.getPaymentTp()))
					&& "10".equals(order.getPaymentSt())) {
				// TODO
				String thirdPartySrc = ((ThirdPartyTransfer) baseMgr.get(ThirdPartyTransfer.class, order.getPaymentId()))
						.getSrc();
				if ("01".equals(thirdPartySrc)) {
					thirdPartyName = "退回银联帐户";
				} else if ("02".equals(thirdPartySrc)) {
					thirdPartyName = "退回财付通帐户";
				} else if ("03".equals(thirdPartySrc)) {
					thirdPartyName = "退回交易原支付银行卡/快钱帐户";
				} else if ("04".equals(thirdPartySrc)) {
					thirdPartyName = "退回交易原支付银行卡/支付宝帐户";
				} else if ("06".equals(thirdPartySrc)) {
					thirdPartyName = "退回交易原支付信用卡/快钱帐户";
				}
			}
			objs.put("thirdPartyName", thirdPartyName);

			mailTemplate = "return_product_confirm";
			objs.put("so", serviceOrder);
			objs.put("orderId", order.getOrderId());
			if (serviceOrder.getRealBonus().compareTo(BigDecimal.ZERO) == 1) {
				objs.put("originalBonus", serviceOrder.getRealBonus().multiply(order.getOrderBonusCampaign().getOffsetRate()));
			}
			objs.put("orderDate", sdf.format(order.getCreateDate()));
			objs.put("paymentType", JspUtil.menuOption("Order.PaymentTp", order.getPaymentTp()).getName());
			objs.put("memberName", order.getMemberName());
			objs.put("serviceOrder", serviceOrder);
			objs.put("serviceId", serviceOrder.getServiceId());
			objs.put("sop", sops[0]);
			objs.put("sops", sops);
			objs.put("domainSrc", order.getDomainSrc());
			Mail entity = velocityService.formatToMail(mailTemplate, objs);
			// System.out.println(entity.getBody());
			Member member = (Member) baseMgr.get(Member.class, serviceOrder.getMemberId());
			entity.addTo(member.getLoginId(), member.getEmail());
			entity.setFrom(envParameters.getEmailFromCs());
			mailService.save(entity);
		}
	}

	private String toXML(Orders order) {

		String xml = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			BeanWriter beanWriter = new BeanWriter(baos);
			beanWriter.enablePrettyPrint();
			beanWriter.write(order);
			xml = baos.toString();
			beanWriter.flush();
			baos.flush();
			beanWriter.close();
			baos.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		return xml;

	}

	private String toXML(ServiceOrders serviceOrder) {

		String xml = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			BeanWriter beanWriter = new BeanWriter(baos);
			beanWriter.enablePrettyPrint();
			beanWriter.write(serviceOrder);
			xml = baos.toString();
			beanWriter.flush();
			baos.flush();
			beanWriter.close();
			baos.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		return xml;

	}

	@Override
	public ServiceOrders update(ServiceOrders serviceOrder) {
		// todo log
		return update(serviceOrder);
	}

	private void updateCobrandingOrder(Orders order) {
		String domainSrc = order.getDomainSrc();
		try {
			if (!domainSrc.endsWith("yaodian100.com")) {
				if (domainSrc.endsWith("onlylady.com")) {
					Onlylady ol = new Onlylady();
					ol.olReturnOrder(order);
				}
			}
		} catch (Exception e) {

			// e.printStackTrace();
		}
	}

	private void updateOrder(ServiceOrders serviceOrder, Collection<ServiceOrderProduct> serviceOrderProducts) {
		Orders order = (Orders) baseMgr.get(Orders.class, serviceOrder.getOrderId());
		if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.CANCEL.getValue()) {
			if (serviceOrder.getReasonTp().equals(ServiceOrders.REASON_TYPE.CSR_UPDATEORDER.getValue())) {
				// 客服取消订单
				order.setOrderSt(Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue());
			} else {
				// user取消整张订单
				if (!Orders.ORDER_ST.CANCEL_BY_NOT_APPROVE.getValue().equals(order.getOrderSt())
						&& !Orders.ORDER_ST.CANCEL_BY_SYSTEM.getValue().equals(order.getOrderSt())) {
					order.setOrderSt(Orders.ORDER_ST.CANCEL_BY_USER.getValue());
				}

			}
			for (OrderProduct op : order.getOrderProduct()) {
				op.setProdStatus(3);
				baseMgr.update(op);
			}
			baseMgr.update(order);
		} else {
			for (ServiceOrderProduct sop : serviceOrderProducts) {
				for (OrderProduct op : order.getOrderProduct()) {
					if (sop.getOrderProdId().equals(op.getOrderProdId())) {
						op.setProdStatus(serviceOrder.getSrcType());
						baseMgr.update(op);
					}
				}
			}

		}
	}

	@Transactional(readOnly = false)
	public void updateServiceOrderStatus(ServiceOrderBo sob) {

		ServiceOrders so = (ServiceOrders) baseMgr.get(ServiceOrders.class, sob.getId());
		if (so.getReturnSt().equals("30") && so.getOrderSt().equals("90") && sob.getRefundDate() != null) {
			// 若订单为已退款，且状态为已完成，且退款日期不为空，则更新服务单及订单的退款时间
			so.setRefundDate(sob.getRefundDate());
			if (sob.getRefundApproveDate() != null) {
				so.setRefundApproveDate(sob.getRefundApproveDate());
			}
			Orders order = (Orders) baseMgr.get(Orders.class, so.getOrderId());
			order.setRefundDate(sob.getRefundApproveDate());
			baseMgr.update(so);
			baseMgr.update(order);
			return;
		}
		if (sob.getStatus() == 0) {
			LOGGER.error("Status of service order is not accessable:00");
			throw new RuntimeException("Status of service order is not accessable:00, serviceId:" + so.getServiceId());
		}
		if (sob.getStatus() == 98) {
			LOGGER.error("Status of service order is unknow:98");
			throw new RuntimeException("Status of service order is unknow:98, serviceId:" + so.getServiceId());
		}
		if (sob.getStatus() == 99) {
			// 當退款狀態為20時，直接避掉
			LOGGER.warn("Status of service order is unknow:99");
			// throw new RuntimeException("Status of service order is unknow:99");
			return;
		}
		if (so.getReturnSt().equals("30")) {
			// 已退款，直接避掉！！
			LOGGER.warn("This service order(" + so.getServiceId() + ") has refunded, it can't be update again!!");
			return;
		}
		so.setRefundApproveDate(sob.getRefundApproveDate());
		so.setRefundDate(sob.getRefundDate());
		if ((so.getOrderSt().equals("40") && so.getReturnSt().equals("30"))
		// || (so.getOrderSt().equals("90") && sob.getStatus() != 40)) {
				|| (so.getOrderSt().equals("90") && so.getReturnSt().equals("30"))) {
			LOGGER.warn("THIS SERVICE ORDER had been finished! It can't be update again!ServiceId:" + so.getServiceId());
			return;
			// throw new RuntimeException("THIS SERVICE ORDER had been finished! It can't be update again!ServiceId:"
			// + so.getServiceId());
		}

		so.setRealBenefit(new BigDecimal(sob.getRealBenefit()));
		so.setRealBonus(new BigDecimal(sob.getRealBonus()));
		so.setRealCash(new BigDecimal(sob.getRealCash()));
		so.setRealMyWallet(new BigDecimal(sob.getRealMyWallet()));
		so.setRealShipfare(new BigDecimal(sob.getRealShipfare()));
		so.setRealInstallmentFee(new BigDecimal(sob.getRealInstallmentFee()));
		so.setRealGiftcardBalance(new BigDecimal(sob.getRealGiftcardBalance()));

		if (so.getReturnBenefit().floatValue() < so.getRealBenefit().floatValue()
				|| so.getReturnBonus().floatValue() < so.getRealBonus().floatValue()
				|| so.getReturnCash().floatValue() < so.getRealCash().floatValue()
				|| so.getReturnMyWallet().floatValue() < so.getRealMyWallet().floatValue()
				|| so.getReturnGiftcardBalance().floatValue() < so.getRealGiftcardBalance().floatValue()) {
			// 退款金额异常丢出例外并寄发mail至fe@yaodian100.com
			Map<String, Object> objs = new HashMap<String, Object>();
			objs.put("so", so);
			Mail entity = velocityService.formatToMail("refundException", objs);
			entity.addTo("fe@yaodian100.com", "fe@yaodian100.com");
			entity.setFrom(envParameters.getEmailFromCs());
			mailService.save(entity);
			throw new RuntimeException("Real refund amount grant than must refund amount, orderId:" + so.getOrderId());
		}

		// 若退款准备中，不异动serviceorder状态
		if (!sob.getReturnStatus().equals("20")) {
			so.setOrderSt(String.valueOf(sob.getStatus()));
		}
		// 加這段判斷的目的是？
		// if (sob.getReturnStatus() == null) {
		so.setReturnSt(sob.getReturnStatus());
		// }

		Orders order = (Orders) baseMgr.get(Orders.class, so.getOrderId());
		BigDecimal returnTotalPrice = order.getReturnTotalPrice();
		if (returnTotalPrice == null) {
			returnTotalPrice = new BigDecimal(0);
		}
		if (so.getSrcType() == ServiceOrders.SERVICE_TYPE.SHIP_EXCEPTION.getValue() && so.getReasonTp() != null
				&& so.getReasonTp().equals(ServiceOrders.REASON_TYPE.USER_RETURN_SHIPFARE.getValue())) {
			// 退还溢收运费
			// 1.记录order memo
			String memo = order.getMemo() != null ? order.getMemo() + "<br/>" : "";
			order.setMemo(memo + "退还溢收运费：￥" + so.getRealMyWallet());

		}
		if (so.getReturnSt().equals("30")) {
			// 將退款金額回寫回訂單
			updateOrderReturn(order.getOrderId(), so.getRealBenefit().floatValue(), so.getRealCash().floatValue(), so
					.getRealMyWallet().floatValue(), so.getRealBonus().floatValue(), so.getRealCoupon().floatValue(), so
					.getRealInstallmentFee().floatValue(), so.getRealGiftcardBalance().floatValue(), so.getRealShipfare()
					.floatValue(), sob.getRefundDate());

		}
		baseMgr.update(order);

		Date logisticFinalDate = new Date();
		Date logisticSignDate = new Date();

		DetachedCriteria dc = DetachedCriteria.forClass(ServiceOrderProduct.class);
		dc.add(Restrictions.eq("serviceId", sob.getId()));
		Object[] sops = baseMgr.findByCriteria(dc);
		String logisticSt = "";
		String logisticNum = "";
		for (Object obj : sops) {
			ServiceOrderProduct sod = (ServiceOrderProduct) obj;
			for (ServiceProduct ssop : sob.getServiceProducts()) {
				if (sod.getServiceProdId().equals(ssop.getId())) {
					logisticNum += "，" + ssop.getLogisticRefNum();
					sod.setLogisticRefNum(ssop.getLogisticRefNum());
					sod.setLogisticRemark(ssop.getLogisticRemark());
					sod.setLogisticCompany(ssop.getLogisticCompany());
					sod.setLogisticSt(ssop.getLogisticSt());
					logisticSt = sod.getLogisticSt();
					logisticFinalDate = ssop.getLogisticFinalDt();
					logisticSignDate = ssop.getLogisticSignDt();

					baseMgr.update(sod);
				}
			}
		}
		if (logisticNum.length() > 2) {
			logisticNum = logisticNum.substring(1);
		}
		if ("30".equals(so.getReceiverTp())) {

			SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
			if (!"30001231".equals(df.format(logisticSignDate))) {
				so.setLogisticSignDt(logisticSignDate);
			}
			if (!"30001231".equals(df.format(logisticFinalDate))) {
				so.setLogisticFinalDt(logisticFinalDate);
			}
		}
		baseMgr.update(so);
		if (so.getReturnSt().equals("30")) {
			// System.out.println("returnSt:" + so.getReturnSt());
			// //服务单完成
			so.setOrderSt("90");
			refundForPaid(so, sob);

		}

		Member member = (Member) baseMgr.get(Member.class, order.getMemberId());
		if (so.getSrcType() == ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
			if (so.getOrderSt().equals("20")) {
				// 確認可退貨通知
				sendMailForReturnNote(order, so, sops);

			} else if (so.getOrderSt().equals("90")) {
				// 退款完成

				sendMailForReturnDone(order, so, sops);
				// 退貨完成時同步cobrand訂單
				updateCobrandingOrder(order);

			} else if (so.getOrderSt().equals("70")) {
				// 退货失败
				sendMailForReturnFail(order, so, sops);
			}
		} else if (so.getSrcType() == ServiceOrders.SERVICE_TYPE.CHANGE.getValue()) {
			if (so.getOrderSt().equals("20")) {
				// 换货发货通知
				sendMailForChangeNote(order, so, sops);
				// send sms
				if (member.getMobile() != null && member.getMobile().length() > 0 && !"30".equals(order.getReceiverTp())) {
					String msg = "您在耀点100的订单" + order.getOrderId() + "，换货商品已发出，将于近日送达！（您可用快递单号：" + logisticNum + "至订单页查询发货进度）";
					smsService.sendSms(member.getMobile(), msg, order.getOrderId());
				}
				// 記錄出貨單號
				for (ServiceProduct product : sob.getServiceProducts()) {
					for (ODetail remoteDetail : product.getDetail()) {
						Long serviceDetailId = Long.valueOf(remoteDetail.getServiceDetailId());
						ServiceOrderDetail detail = this.serviceOrderDetailDao.get(serviceDetailId);
						if (detail != null) {
							String shippingOid = remoteDetail.getShippingOid();
							detail.setShippingOid(shippingOid);
							String shippingLineOidString = remoteDetail.getShippingLineOid();
							Long shippingLineOid = null;
							if (shippingLineOidString != null) {
								shippingLineOid = Long.valueOf(remoteDetail.getShippingLineOid());
							}
							detail.setShippingLineOid(shippingLineOid);
							this.serviceOrderDetailDao.update(detail);

							Long orderDetailId = detail.getOrderDetailId();
							OrderProductDetail orderDetail = this.orderProductDetailDao.get(orderDetailId);
							if (orderDetail != null) {
								orderDetail.setShippingOid(shippingOid);
								orderDetail.setShippingLineOid(shippingLineOid);
								this.orderProductDetailDao.update(orderDetail);
							}

						}
					}
				}
			} else if (so.getOrderSt().equals("90")) {
				// 换货完成

				// sendMailForChangeDone(order, so, sops);
				if (so.getReturnSt().equals("30")) {
					sendMailForReturnDone(order, so, sops);
					// 退貨完成時同步cobrand訂單
					updateCobrandingOrder(order);
				}

			} else if (so.getOrderSt().equals("70")) {
				// 换货失败
				sendMailForChangeFail(order, so, sops);
			} else if (so.getOrderSt().equals("40") && so.getReturnSt().equals("30")) {
				// 换货转退货
				sendMailForRefund(order, so, sops);
			} else if (so.getOrderSt().equals("25")) {
				dpnService.sendNotice("order.arriveStore", order, so);
			}

		} else if (so.getSrcType() == ServiceOrders.SERVICE_TYPE.CANCEL.getValue()) {
			if (so.getOrderSt().equals("90")) {

				// 退款完成

				sendMailForRefund(order, so, sops);
			}
		}

	}

	private ServiceOrders orderMaster(ServiceOrders serviceOrder, String memberId, String accessaryDescription,
			String paymentTp, String receiverName, String receiverAddr, String receiverArea, String receiverCity,
			String receiverExt, String clientIp, String receiverMobile, String receiverPhone, String receiverPhoneArea,
			String receiverProvince, String receiverZip, String reason, String description, String srcType, String orderId,
			String b2eId) {
		Member member = null;
		member = (Member) baseMgr.get(Member.class, memberId);
		receiverArea = receiverArea.split(",")[0];
		// 配件状况
		serviceOrder.setAccessaryDescription(accessaryDescription);
		Date date = new Date();
		serviceOrder.setCreateDate(date);
		serviceOrder.setCreateUser("SYSTEM");
		serviceOrder.setLoginId(member.getLoginId());
		serviceOrder.setMemberId(member.getMemberId());
		serviceOrder.setMemberName(member.getName());
		serviceOrder.setMemo(" ");
		serviceOrder.setOrderSt("10");
		serviceOrder.setReturnSt("00");
		serviceOrder.setReasonTp("0");
		serviceOrder.setReturnBenefit(new BigDecimal(0));
		serviceOrder.setReturnCodFee(new BigDecimal(0));
		serviceOrder.setReturnBonus(new BigDecimal(0));
		serviceOrder.setReturnCoupon(new BigDecimal(0));
		serviceOrder.setReturnCash(new BigDecimal(0));
		serviceOrder.setReturnMyWallet(new BigDecimal(0));
		serviceOrder.setReturnShipfare(new BigDecimal(0));
		serviceOrder.setRealBenefit(new BigDecimal(0));
		serviceOrder.setRealBonus(new BigDecimal(0));
		serviceOrder.setRealCoupon(new BigDecimal(0));
		serviceOrder.setRealCash(new BigDecimal(0));
		serviceOrder.setRealMyWallet(new BigDecimal(0));
		serviceOrder.setRealShipfare(new BigDecimal(0));
		serviceOrder.setRealCodFee(new BigDecimal(0));
		serviceOrder.setReturnInstallmentFee(new BigDecimal(0));
		serviceOrder.setRealInstallmentFee(new BigDecimal(0));

		serviceOrder.setPaymentTp(paymentTp);
		serviceOrder.setModifyUser("SYSTEM");
		serviceOrder.setServiceDt(date);
		serviceOrder.setReceiverName(receiverName);
		serviceOrder.setReceiverAddr(receiverAddr);
		serviceOrder.setReceiverArea(receiverArea);
		serviceOrder.setReceiverCity(receiverCity);
		if (receiverExt == null || receiverExt.length() == 0) {
			receiverExt = " ";
		}
		if (receiverMobile == null || receiverMobile.length() == 0) {
			receiverMobile = " ";
		}
		if (receiverPhone == null || receiverPhone.length() == 0) {
			receiverPhone = " ";
		}
		if (receiverPhoneArea == null || receiverPhoneArea.length() == 0) {
			receiverPhoneArea = " ";
		}
		serviceOrder.setReceiverExt(receiverExt);
		serviceOrder.setReceiverMobile(receiverMobile);
		serviceOrder.setReceiverPhone(receiverPhone);
		serviceOrder.setReceiverPhoneArea(receiverPhoneArea);

		serviceOrder.setReceiverProvince(receiverProvince);
		serviceOrder.setReceiverZip(receiverZip);
		serviceOrder.setReason(reason);
		serviceOrder.setDescription(description);
		serviceOrder.setSrcType(Integer.parseInt(srcType));
		serviceOrder.setOrderId(orderId);
		serviceOrder.setClientIp(clientIp);
		serviceOrder.setB2eId(b2eId);
		serviceOrder.setReceiverEmail(member.getEmail());

		Orders order = (Orders) baseMgr.get(Orders.class, orderId);

		serviceOrder.setCodFee(order.getCodFee());
		serviceOrder.setReturnCodFee(serviceOrder.getCodFee());
		serviceOrder.setRealCodFee(serviceOrder.getCodFee());

		serviceOrder = setRetunAddressInfo(serviceOrder, order);

		return serviceOrder;
	}

	/**
	 * @param serviceOrder
	 * @param order
	 * @return
	 */
	private ServiceOrders setRetunAddressInfo(ServiceOrders serviceOrder, Orders order) {
		String orderId = order.getOrderId();
		SupplierApply defaultSupplier = supplierApplyService.get("000000");
		// scm退貨
		if ("2".equals(order.getOrderType())) {
			boolean hasError = false;
			StringBuffer errorStr = new StringBuffer();

			if (StringUtils.isNotBlank(order.getScmSupplierId())) {
				SupplierApply supplier = supplierApplyService.get(order.getScmSupplierId());

				if (null != supplier) {
					serviceOrder.setIsReturnSupplier(supplier.getIsReturnSupplier());
					serviceOrder.setIsSelfShip(supplier.getIsSelfShip());
					// 為供應商自行配送且商品為直退/換
					if ("Y".equalsIgnoreCase(supplier.getIsSelfShip()) && "Y".equalsIgnoreCase(supplier.getIsReturnSupplier())) {
						// 地址欄位齊全
						if (StringUtils.isNotBlank(supplier.getReturnAddr()) && StringUtils.isNotBlank(supplier.getReturnArea())
								&& StringUtils.isNotBlank(supplier.getReturnCity())
								&& StringUtils.isNotBlank(supplier.getReturnContact())
								&& StringUtils.isNotBlank(supplier.getReturnPhone())
								&& StringUtils.isNotBlank(supplier.getReturnProvince())
								&& StringUtils.isNotBlank(supplier.getReturnZip())) {

							serviceOrder.setReturnAddr(supplier.getReturnAddr());
							serviceOrder.setReturnArea(supplier.getReturnArea());
							serviceOrder.setReturnCity(supplier.getReturnCity());
							serviceOrder.setReturnContact(supplier.getReturnContact());
							serviceOrder.setReturnPhone(supplier.getReturnPhone());
							serviceOrder.setReturnProvince(supplier.getReturnProvince());
							serviceOrder.setReturnZip(supplier.getReturnZip());

						} else {
							// 退換貨地址異常，使用預設退換貨地址
							serviceOrder.setReturnAddr(defaultSupplier.getReturnAddr());
							serviceOrder.setReturnArea(defaultSupplier.getReturnArea());
							serviceOrder.setReturnCity(defaultSupplier.getReturnCity());
							serviceOrder.setReturnContact(defaultSupplier.getReturnContact());
							serviceOrder.setReturnPhone(defaultSupplier.getReturnPhone());
							serviceOrder.setReturnProvince(defaultSupplier.getReturnProvince());
							serviceOrder.setReturnZip(defaultSupplier.getReturnZip());
							hasError = true;
							errorStr.append("订单编号:").append(orderId).append("，供应商编号:").append(order.getScmSupplierId());
							errorStr.append("退换货地址异常，已采用供应商编号000000的退换货地址");
						}
					} else {// 退到我方倉庫，使用預設退換貨地址
						serviceOrder.setReturnAddr(defaultSupplier.getReturnAddr());
						serviceOrder.setReturnArea(defaultSupplier.getReturnArea());
						serviceOrder.setReturnCity(defaultSupplier.getReturnCity());
						serviceOrder.setReturnContact(defaultSupplier.getReturnContact());
						serviceOrder.setReturnPhone(defaultSupplier.getReturnPhone());
						serviceOrder.setReturnProvince(defaultSupplier.getReturnProvince());
						serviceOrder.setReturnZip(defaultSupplier.getReturnZip());
					}
				} else {
					// 供應商找不到異常，使用預設退換貨地址
					serviceOrder.setIsReturnSupplier("N");
					serviceOrder.setIsSelfShip("N");

					serviceOrder.setReturnAddr(defaultSupplier.getReturnAddr());
					serviceOrder.setReturnArea(defaultSupplier.getReturnArea());
					serviceOrder.setReturnCity(defaultSupplier.getReturnCity());
					serviceOrder.setReturnContact(defaultSupplier.getReturnContact());
					serviceOrder.setReturnPhone(defaultSupplier.getReturnPhone());
					serviceOrder.setReturnProvince(defaultSupplier.getReturnProvince());
					serviceOrder.setReturnZip(defaultSupplier.getReturnZip());
					hasError = true;
					errorStr.append("订单编号:").append(orderId).append("，供应商编号:").append(order.getScmSupplierId());
					errorStr.append("供应商编号不存在，已采用供应商编号000000的退换货地址");
				}
			} else {
				// 供應商編號未寫入異常
				serviceOrder.setIsReturnSupplier("N");
				serviceOrder.setIsSelfShip("N");

				serviceOrder.setReturnAddr(defaultSupplier.getReturnAddr());
				serviceOrder.setReturnArea(defaultSupplier.getReturnArea());
				serviceOrder.setReturnCity(defaultSupplier.getReturnCity());
				serviceOrder.setReturnContact(defaultSupplier.getReturnContact());
				serviceOrder.setReturnPhone(defaultSupplier.getReturnPhone());
				serviceOrder.setReturnProvince(defaultSupplier.getReturnProvince());
				serviceOrder.setReturnZip(defaultSupplier.getReturnZip());
				hasError = true;
				errorStr.append("订单编号:").append(orderId).append("，SCM订单但供应商编号未写入");
				errorStr.append("，已采用供应商编号000000的退换货地址");
			}
			// 外部商城訂單不需寄mail
			if (StringUtils.isBlank(serviceOrder.getOuterOrderId())) {
				if (hasError) {
					Mail mail = new Mail("服务单退换货地址异常", errorStr.toString(), "fe@yaodian100.com", "");
					mail.setFrom(envParameters.getEmailFromSystem());
					mailService.save(mail);
				}
			}
		} else {// 其他種類訂單的預設值
			serviceOrder.setIsReturnSupplier("N");
			serviceOrder.setIsSelfShip("N");

			serviceOrder.setReturnAddr(defaultSupplier.getReturnAddr());
			serviceOrder.setReturnArea(defaultSupplier.getReturnArea());
			serviceOrder.setReturnCity(defaultSupplier.getReturnCity());
			serviceOrder.setReturnContact(defaultSupplier.getReturnContact());
			serviceOrder.setReturnPhone(defaultSupplier.getReturnPhone());
			serviceOrder.setReturnProvince(defaultSupplier.getReturnProvince());
			serviceOrder.setReturnZip(defaultSupplier.getReturnZip());
		}
		return serviceOrder;
	}

	@Transactional(readOnly = false)
	public String returnAllProduct(String orderId, String memberId, String[] orderProdId, String[] accessaryDescription,
			String[] reason, String[] description, String receiverName, String receiverAddr, String receiverArea,
			String receiverCity, String receiverExt, String clientIp, String receiverMobile, String receiverPhone,
			String receiverPhoneArea, String receiverProvince, String receiverZip, String b2eId) {
		String message = "";
		Orders order = (Orders) baseMgr.get(Orders.class, orderId);
		long serviceOrderCount = baseMgr.countByHql("from ServiceOrders where orderId=?", new Object[] { orderId });
		Object[] sos = baseMgr.queryByHql("from ServiceOrders where orderId=?", new Object[] { orderId });
		if (serviceOrderCount > 0) {

			if (serviceOrderCount == 1
					&& ((ServiceOrders) sos[0]).getReasonTp().equals(ServiceOrders.REASON_TYPE.USER_RETURN_SHIPFARE.getValue())) {
				// 退還運費
			} else {
				message = "已有退换货或取消单";
				return message;
			}
		}

		for (int mi = 0; mi < orderProdId.length; mi++) {
			OrderProduct op = null;
			for (int mj = 0; mj < order.getOrderProduct().size(); mj++) {
				Long opId = order.getOrderProduct().get(mj).getOrderProdId();
				if (opId.longValue() == Long.valueOf(orderProdId[mi]).longValue()) {
					op = order.getOrderProduct().get(mj);
					break;
				}
			}

			ServiceOrders serviceOrder = orderMaster(new ServiceOrders(), memberId, accessaryDescription[mi], order
					.getPaymentTp(), receiverName, receiverAddr, receiverArea, receiverCity, receiverExt, clientIp,
					receiverMobile, receiverPhone, receiverPhoneArea, receiverProvince, receiverZip, reason[mi], description[mi],
					String.valueOf(ServiceOrders.SERVICE_TYPE.RETURN.getValue()), orderId, b2eId);
			serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.RETURN.getValue());
			// 直接转为申请中
			serviceOrder.setOrderSt("10");

			Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();

			serviceOrder.setOrderId(op.getOrderId());

			ServiceOrderProduct sop = new ServiceOrderProduct();
			BeanUtils.copyProperties(op, sop);
			sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
			// 攤開
			for (OrderProductDetail opd : op.getOrderProductDetail()) {
				for (int opdi = 0; opdi < opd.getQty(); opdi++) {
					ServiceOrderDetail sod = new ServiceOrderDetail();
					BeanUtils.copyProperties(opd, sod);
					sod.setOrgDetailSeq(opd.getDetailSeq());
					sod.setOrgProdSpecId(opd.getProdSpecId());
					sod.setOrgProdSpecName(opd.getProdSpecName());
					sod.setQty(1);
					sop.getServiceOrderDetail().add(sod);
				}
			}
			serviceOrderProducts.add(sop);

			// // 可退总金额
			// BigDecimal returnAmount = ReturnProductRule.calculateReturnAmount(op.getTotalPrice(), op.getCoupon(), op
			// .getDiscount());
			// Return ret = ReturnProductRule.calculateReturn(op.getBonus(), op.getMyWallet(), BigDecimal.ZERO,
			// op.getMustPay(),
			// op.getShipfare(), returnAmount, op.getInstallmentFee(), op.getGiftcardBalance());
			serviceOrder.setServiceOrderProducts(serviceOrderProducts);
			Return ret = calculateReturnProduct(serviceOrder, order);
			serviceOrder.setReturnBenefit(ret.getBenefit());
			serviceOrder.setReturnBonus(ret.getBonus());

			// //第三方支付才会退回cash，其馀全加至账户余额
			serviceOrder.setReturnCash(ret.getCash());
			serviceOrder.setReturnMyWallet(ret.getMyWallet());

			// 退貨不退coupon
			serviceOrder.setReturnCoupon(new BigDecimal(0));
			serviceOrder.setRealCoupon(new BigDecimal(0));

			// 禮品卡計算
			serviceOrder.setReturnGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setRealGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setGiftcardBalanceShipfare(BigDecimal.ZERO);

			serviceOrder.setRealBenefit(ret.getBenefit());
			serviceOrder.setRealBonus(ret.getBonus());
			serviceOrder.setRealCash(ret.getCash());
			serviceOrder.setRealMyWallet(ret.getMyWallet());
			serviceOrder.setOrderType(order.getOrderType());
			serviceOrder.setScmSupplierId(order.getScmSupplierId());

			// 退還手續費
			serviceOrder.setReturnInstallmentFee(ret.getInstallmentFee());
			serviceOrder.setRealInstallmentFee(ret.getInstallmentFee());
			// 檢查金額是否有問題
			checkServiceOrderAmount(serviceOrder, order);

			saveServiceOrders(order, serviceOrder, serviceOrderProducts, true);

			op.setProdStatus(serviceOrder.getSrcType());
			baseMgr.update(op);
			serviceOrder.setServiceOrderProducts(serviceOrderProducts);
			batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(serviceOrder)
					+ "</root>", null, serviceOrder.getServiceId());

		}

		return message;
	}

	// 目前沒有被呼叫
	public Return calculateReturn(String orderProdId, String[] prodSpecIds, String[] qtys) {
		OrderProduct op = (OrderProduct) baseMgr.get(OrderProduct.class, Long.parseLong(orderProdId));
		ServiceOrders so = new ServiceOrders();
		ServiceOrderProduct sop = new ServiceOrderProduct();
		BeanUtils.copyProperties(op, sop);
		so.setServiceOrderProducts(new ArrayList<ServiceOrderProduct>());
		so.getServiceOrderProducts().add(sop);
		for (int i = 0; i < prodSpecIds.length; i++) {
			String prodSpecId = prodSpecIds[i];
			String qty = qtys[i];

			if (qty.equals("")) {
				continue;
			}

			DetachedCriteria dc = DetachedCriteria.forClass(OrderProductDetail.class);
			dc.add(Restrictions.eq("orderId", op.getOrderId()));
			dc.add(Restrictions.eq("orderProdId", Long.parseLong(orderProdId)));
			dc.add(Restrictions.eq("prodSpecId", prodSpecId));

			Object[] orderProductDetails = baseMgr.findByCriteria(dc);

			for (int j = 0; j < Integer.parseInt(qty); j++) {

				OrderProductDetail opd = (OrderProductDetail) orderProductDetails[0];
				ServiceOrderDetail sod = new ServiceOrderDetail();
				BeanUtils.copyProperties(opd, sod);
				sod.setOrgDetailSeq(opd.getDetailSeq());
				sod.setOrgProdSpecId(opd.getProdSpecId());
				sod.setOrgProdSpecName(opd.getProdSpecName());
				sod.setProdSpecId(prodSpecIds[i]);
				sod.setQty(1);
				Specification spec = (Specification) baseMgr.get(Specification.class, Long.parseLong(prodSpecIds[i]));
				sod.setProdSpecName(spec.getSpecificationName());
				if (sop.getServiceOrderDetail() == null) {
					sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
				}
				sop.getServiceOrderDetail().add(sod);
			}
		}

		Orders order = (Orders) baseMgr.get(Orders.class, op.getOrderId());
		Return ret = calculateReturnProduct(so, order);
		return ret;
	}

	/**
	 * serviceOrder必须是已放入欲退货或欲换货之内容（直接将orderProduct/orderDetail汇入serviceOrderProduct/serviceOrderDetail, 不需先运算）
	 * 
	 * @param so
	 * @param order
	 * @return
	 */
	public Return calculateReturnProduct(ServiceOrders so, Orders order) {
		try {
			BigDecimal totalPrice = BigDecimal.ZERO;
			BigDecimal discount = BigDecimal.ZERO;
			BigDecimal coupon = BigDecimal.ZERO;
			BigDecimal bonus = BigDecimal.ZERO;
			BigDecimal giftcard = BigDecimal.ZERO;
			BigDecimal myWallet = BigDecimal.ZERO;
			BigDecimal cash = BigDecimal.ZERO;
			BigDecimal installmentfee = BigDecimal.ZERO;

			BigDecimal bonusSrc = BigDecimal.ZERO;
			BigDecimal giftcardSrc = BigDecimal.ZERO;
			BigDecimal myWalletSrc = BigDecimal.ZERO;
			BigDecimal cashSrc = BigDecimal.ZERO;
			BigDecimal installmentfeeSrc = BigDecimal.ZERO;
			BigDecimal tmpOpFee = BigDecimal.ZERO;
			BigDecimal tmpOpdFee = BigDecimal.ZERO;
			Map<Long, OrderProduct> opMap = new HashMap<Long, OrderProduct>();
			Map<Long, OrderProductDetail> opdMap = new HashMap<Long, OrderProductDetail>();
			// 補跑訂單金額分配資料
			if (StringUtils.isBlank(order.getTranslateFlag())) {
				order.setTranslateFlag("Y");
				order = orderService.calcOrderProduct(order);
				order = orderService.calcOrderCash(order);
				orderService.update(order);
			}
			// 外部商城訂單允許到小數點二位數
			int dot = 1;
			if ("3".equals(order.getOrderType())) {
				dot = 2;
			}
			for (OrderProduct op : order.getOrderProduct()) {
				opMap.put(op.getOrderProdId(), op);
				for (OrderProductDetail opd : op.getOrderProductDetail()) {
					opdMap.put(opd.getOrderDetailId(), opd);
				}
			}
			OrderProduct tmpOp = null;
			OrderProductDetail tmpOpd = null;
			// 退款不退运费、cod手续费
			// 在计算账户余额、礼品卡、现金时，应看该订单是使用哪一种来支付附加费用，并将其扣除后再计算
			for (Object sopObj : so.getServiceOrderProducts()) {
				ServiceOrderProduct sop = (ServiceOrderProduct) sopObj;
				tmpOp = opMap.get(sop.getOrderProdId());
				tmpOpFee = tmpOp.getShipfare().add(tmpOp.getCodFee()).add(tmpOp.getInstallmentFee());
				bonusSrc = bonusSrc.add(tmpOp.getBonus());

				if (order.getGiftcardBalanceShipfare().compareTo(BigDecimal.ZERO) == 1) {
					giftcardSrc = giftcardSrc.add(tmpOp.getGiftcardBalance()).subtract(tmpOpFee);
				} else {
					giftcardSrc = giftcardSrc.add(tmpOp.getGiftcardBalance());
				}

				if (order.getMyWalletShipfare().compareTo(BigDecimal.ZERO) == 1) {
					myWalletSrc = myWalletSrc.add(tmpOp.getMyWallet()).subtract(tmpOpFee);
				} else {
					myWalletSrc = myWalletSrc.add(tmpOp.getMyWallet());
				}
				if (tmpOpFee.compareTo(BigDecimal.ZERO) == 1 && order.getMyWalletShipfare().compareTo(BigDecimal.ZERO) == 0
						&& order.getGiftcardBalanceShipfare().compareTo(BigDecimal.ZERO) == 0) {
					cashSrc = cashSrc.add(tmpOp.getCash()).subtract(tmpOpFee);
				} else {
					cashSrc = cashSrc.add(tmpOp.getCash());
				}
				installmentfeeSrc = installmentfeeSrc.add(tmpOp.getInstallmentFee());
				for (Object sodObj : sop.getServiceOrderDetail()) {
					ServiceOrderDetail sod = (ServiceOrderDetail) sodObj;
					tmpOpd = opdMap.get(sod.getOrderDetailId());
					if (null != tmpOpd) {
						tmpOpdFee = tmpOpd.getShipfare().add(tmpOpd.getCodFee()).add(tmpOpd.getInstallmentFee());
						// 不论全退或部份退，都先按比例算
						if (!"6".equals(tmpOpd.getDetailTp())) {
							if (tmpOpd.getUnitPrice().compareTo(BigDecimal.ZERO) == 1) {
								totalPrice = totalPrice.add(tmpOpd.getUnitPrice());
								coupon = coupon.add(tmpOpd.getCoupon().multiply(tmpOpd.getUnitPrice()).divide(tmpOp.getTotalPrice(),
										dot, BigDecimal.ROUND_CEILING));
								discount = discount.add(tmpOpd.getDiscount().multiply(tmpOpd.getUnitPrice()).divide(
										tmpOp.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								bonus = bonus.add(tmpOpd.getBonus().multiply(tmpOpd.getUnitPrice()).divide(tmpOp.getTotalPrice(), dot,
										BigDecimal.ROUND_CEILING));
								if (order.getMyWalletShipfare().compareTo(BigDecimal.ZERO) == 1) {
									myWallet = myWallet.add((tmpOpd.getMyWallet().subtract(tmpOpdFee)).multiply(tmpOpd.getUnitPrice())
											.divide(tmpOp.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								} else {
									myWallet = myWallet.add((tmpOpd.getMyWallet().multiply(tmpOpd.getUnitPrice())).divide(tmpOp
											.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								}
								if (order.getGiftcardBalanceShipfare().compareTo(BigDecimal.ZERO) == 1) {
									giftcard = giftcard.add((tmpOpd.getGiftcardBalance().subtract(tmpOpdFee)).multiply(
											tmpOpd.getUnitPrice()).divide(tmpOp.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								} else {
									giftcard = giftcard.add(tmpOpd.getGiftcardBalance().multiply(tmpOpd.getUnitPrice()).divide(
											tmpOp.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								}
								installmentfee = installmentfee.add(tmpOpd.getInstallmentFee().multiply(tmpOpd.getUnitPrice()).divide(
										tmpOp.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								if (tmpOpdFee.compareTo(BigDecimal.ZERO) == 1
										&& order.getMyWalletShipfare().compareTo(BigDecimal.ZERO) == 0
										&& order.getGiftcardBalanceShipfare().compareTo(BigDecimal.ZERO) == 0) {
									cash = cash.add((tmpOpd.getCash().subtract(tmpOpdFee)).multiply(tmpOpd.getUnitPrice()).divide(
											tmpOp.getTotalPrice(), dot, BigDecimal.ROUND_CEILING));
								} else {
									cash = cash.add(tmpOpd.getCash().multiply(tmpOpd.getUnitPrice()).divide(tmpOp.getTotalPrice(), dot,
											BigDecimal.ROUND_CEILING));
								}
							}
						} else {
							totalPrice = totalPrice.add(tmpOpd.getAmount());
							coupon = coupon.add(tmpOpd.getCoupon());
							discount = discount.add(tmpOpd.getDiscount());
							bonus = bonus.add(tmpOpd.getBonus());
							if (order.getMyWalletShipfare().compareTo(BigDecimal.ZERO) == 1) {
								myWallet = myWallet.add(tmpOpd.getMyWallet()).subtract(tmpOpdFee);
							} else {
								myWallet = myWallet.add(tmpOpd.getMyWallet());
							}
							if (order.getGiftcardBalanceShipfare().compareTo(BigDecimal.ZERO) == 1) {
								giftcard = giftcard.add(tmpOpd.getGiftcardBalance()).subtract(tmpOpdFee);
							} else {
								giftcard = giftcard.add(tmpOpd.getGiftcardBalance());
							}
							installmentfee = installmentfee.add(tmpOpd.getInstallmentFee());
							if (tmpOpdFee.compareTo(BigDecimal.ZERO) == 1
									&& order.getMyWalletShipfare().compareTo(BigDecimal.ZERO) == 0
									&& order.getGiftcardBalanceShipfare().compareTo(BigDecimal.ZERO) == 0) {
								cash = cash.add(tmpOpd.getCash()).subtract(tmpOpdFee);
							} else {
								cash = cash.add(tmpOpd.getCash());
							}
						}
					}
				}
			}
			Return ret = new Return();
			ret.setBenefit(new BigDecimal(0));
			if (bonus.compareTo(bonusSrc) == 1) {// 避免全退的金额大于原金额
				ret.setBonus(bonusSrc);
			} else {
				ret.setBonus(bonus);
			}
			if (cash.compareTo(cashSrc) == 1) {// 避免全退的金额大于原金额
				ret.setCash(cashSrc);
			} else {
				ret.setCash(cash);
			}
			if (myWallet.compareTo(myWalletSrc) == 1) {// 避免全退的金额大于原金额
				ret.setMyWallet(myWalletSrc);
			} else {
				ret.setMyWallet(myWallet);
			}
			if (giftcard.compareTo(giftcardSrc) == 1) {// 避免全退的金额大于原金额
				ret.setGiftcardBalance(giftcardSrc);
			} else {
				ret.setGiftcardBalance(giftcard);
			}
			if (installmentfee.compareTo(installmentfeeSrc) == 1) {// 避免全退的金额大于原金额
				ret.setInstallmentFee(installmentfeeSrc);
			} else {
				ret.setInstallmentFee(installmentfee);
			}
			return ret;
		} catch (RuntimeException e) {
			e.printStackTrace();
			throw e;

		}
	}

	@Transactional(readOnly = false)
	public ServiceOrderHelper returnProductByOrderProdId(String orderProdId, String memberId,
			String accessaryDescription, String paymentTp, String receiverName, String receiverAddr, String receiverArea,
			String receiverCity, String receiverExt, String clientIp, String receiverMobile, String receiverPhone,
			String receiverPhoneArea, String receiverProvince, String receiverZip, String reason, String description,
			String srcType, String orderId, String b2eId, String[] prodSpecIds, String[] qtys, String orderSt,
			String oldServiceId, String orderDetailId) {
		ServiceOrderHelper soh = new ServiceOrderHelper();

		Object[] sops = baseMgr.queryByHql("from ServiceOrderProduct where orderProdId=?", new Object[] { orderProdId });

		List serviceIdList = new ArrayList();
		for (Object obj : sops) {
			ServiceOrderProduct sop = (ServiceOrderProduct) obj;

			if (serviceIdList.size() > 0) {
				boolean isExist = false;
				for (int i = 0; i < serviceIdList.size(); i++) {
					if (serviceIdList.get(i).equals(sop.getServiceId())) {
						isExist = true;
					}
				}
				if (!isExist) {
					serviceIdList.add(sop.getServiceId());
				}
			} else {
				serviceIdList.add(sop.getServiceId());
			}
		}
		for (int i = 0; i < serviceIdList.size(); i++) {
			ServiceOrders so_ = (ServiceOrders) baseMgr.get(ServiceOrders.class, (String) serviceIdList.get(i));
			if (so_ != null && so_.getReasonTp() != null
					&& so_.getReasonTp().equals(ServiceOrders.REASON_TYPE.USER_RETURN_SHIPFARE.getValue())) {
				// 退運費
			} else {

				soh.setSo(so_);
			}

		}

		boolean hasRC = false;
		boolean nPickMReturnable = true;
		Long orderDetailIdLong = null;

		ServiceOrderDetail serviceOrderDetailOfNPickM = null;
		if (StringUtils.isNotBlank(orderDetailId)) {
			orderDetailIdLong = Long.valueOf(orderDetailId);
			serviceOrderDetailOfNPickM = this.latestServiceOrderDetailByOrderDetailId(orderDetailIdLong);
			nPickMReturnable = this.isOrderDetailReturnableChangable(orderDetailIdLong);
		}

		if (soh.getSo() != null) {
			hasRC = true;
		}

		if ((hasRC && oldServiceId == null) || !nPickMReturnable) {
			String serviceId = null;
			if (!nPickMReturnable) {
				serviceId = serviceOrderDetailOfNPickM.getServiceId();
			} else {
				serviceId = soh.getSo().getServiceId();
			}
			// 已退貸

			Object[] sos = baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new Object[] { serviceId });
			List list = new ArrayList();
			for (Object obj : sos) {
				list.add(obj);
			}
			soh.setServiceOrderProducts(list);
			if (soh.getSo().getSrcType() == ServiceOrders.SERVICE_TYPE.CHANGE.getValue()) {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue());
			} else if (soh.getSo().getSrcType() == ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.getValue());
			} else {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CANCEL.getValue());
			}
			return soh;
		} else {
			// 尚未退货，或是退貨後換貨
			ServiceOrders serviceOrder = orderMaster(new ServiceOrders(), memberId, accessaryDescription, paymentTp,
					receiverName, receiverAddr, receiverArea, receiverCity, receiverExt, clientIp, receiverMobile, receiverPhone,
					receiverPhoneArea, receiverProvince, receiverZip, reason, description, srcType, orderId, b2eId);
			serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.RETURN.getValue());
			if (orderSt.equals("10")) {
				serviceOrder.setOrderSt("10");
			} else {
				serviceOrder.setOrderSt("00");
			}

			Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();

			OrderProduct op = (OrderProduct) baseMgr.get(OrderProduct.class, Long.parseLong(orderProdId));
			serviceOrder.setOrderId(op.getOrderId());
			Integer mainProdReturnQty = 0;

			ServiceOrderProduct sop = new ServiceOrderProduct();
			BeanUtils.copyProperties(op, sop);

			sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
			BigDecimal sumGiftCard = BigDecimal.ZERO;
			for (int i = 0; i < prodSpecIds.length; i++) {
				String prodSpecId = prodSpecIds[i];
				String qty = qtys[i];

				if (qty.equals("")) {
					continue;
				}
				// 判斷是否已有退換貨
				DetachedCriteria dc = DetachedCriteria.forClass(ServiceOrderProduct.class);
				dc.add(Restrictions.eq("orderId", op.getOrderId()));
				dc.add(Restrictions.eq("orderProdId", op.getOrderProdId()));
				dc.add(Restrictions.eq("prodSeq", op.getProdSeq()));
				Object[] serviceOrderProductsCheck = baseMgr.findByCriteria(dc);
				if (serviceOrderProductsCheck != null && serviceOrderProductsCheck.length > 0 && !nPickMReturnable) {
					ServiceOrderProduct sopCheck = (ServiceOrderProduct) serviceOrderProductsCheck[0];
					List<ServiceOrders> serviceOrderListOfOrderProductId = getServiceOrderListByOrderProductId(sopCheck
							.getOrderProdId());
					boolean returnServiceOrderExist = false;
					for (ServiceOrders oneServiceOrder : serviceOrderListOfOrderProductId) {
						if (oneServiceOrder.getSrcType() == ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.value) {
							returnServiceOrderExist = true;
							break;
						}
					}
					if (returnServiceOrderExist) {
						String serviceId = null;
						if (!nPickMReturnable) {
							serviceId = serviceOrderDetailOfNPickM.getServiceId();
						} else {
							serviceId = soh.getSo().getServiceId();
						}

						Object[] sops2 = baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?",
								new Object[] { serviceId });
						List<ServiceOrderProduct> list = new ArrayList<ServiceOrderProduct>();
						for (Object obj : sops2) {
							ServiceOrderProduct sopi = (ServiceOrderProduct) obj;
							list.add(sopi);
						}
						if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.CHANGE.getValue()) {
							soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue());
						} else if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
							soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.getValue());
						} else {
							soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CANCEL.getValue());
						}
						return soh;
					}
				}

				Object[] orderProductDetails = null;
				// if (!op.getProdTp().equals(Product.PRODUCT_TYPE.NPICKM.getValue())) {
				dc = DetachedCriteria.forClass(OrderProductDetail.class);
				dc.add(Restrictions.eq("orderId", op.getOrderId()));
				dc.add(Restrictions.eq("orderProdId", Long.parseLong(orderProdId)));
				dc.add(Restrictions.eq("prodSpecId", prodSpecId));
				orderProductDetails = baseMgr.findByCriteria(dc);
				BigDecimal b4LastGiftAmount = BigDecimal.ZERO;
				for (int j = 0; j < Integer.parseInt(qty); j++) {
					// 攤開
					ServiceOrderDetail sod = new ServiceOrderDetail();
					OrderProductDetail opd = (OrderProductDetail) orderProductDetails[0];
					BeanUtils.copyProperties(opd, sod, IGNORE_PROPERTIES_WHEN_COPY_FROM_ORDER_DETAIL_TO_SERVICE_DETAIL);
					sod.setOrgDetailSeq(opd.getDetailSeq());

					if (StringUtils.isBlank(oldServiceId)) {
						sod.setOrgProdSpecId(opd.getProdSpecId());
						sod.setOrgProdSpecName(opd.getProdSpecName());
					} else {
						// 換貨後退貨，prodSpecId 和 prodSpecName 要寫入最後一次換貨後的值
						ServiceOrderDetail latestServiceOrderDetail = this.latestServiceOrderDetailByOrderDetailId(sod
								.getOrderDetailId());
						sod.setOrgProdSpecId(latestServiceOrderDetail.getProdSpecId());
						sod.setOrgProdSpecName(latestServiceOrderDetail.getProdSpecName());
					}

					// 禮品卡的退款，以退貨單和換貨單來說serviceorder裡每個數量都是1，所以直接除數量
					if (null != opd.getGiftcardBalance()) {
						BigDecimal unitGiftAmount = opd.getGiftcardBalance().divide(new BigDecimal(opd.getQty()),
								BigDecimal.ROUND_HALF_UP).setScale(1);
						if (j == opd.getQty() - 1) {
							sod.setGiftcardBalance(opd.getGiftcardBalance().subtract(b4LastGiftAmount).setScale(1));
						} else {

							sod.setGiftcardBalance(unitGiftAmount);
							b4LastGiftAmount = b4LastGiftAmount.add(unitGiftAmount);

						}
						sumGiftCard = sumGiftCard.add(unitGiftAmount);
					}
					sod.setOrgShippingOid(opd.getShippingOid());
					sod.setOrgShippingLineOid(opd.getShippingLineOid());
					sod.setQty(1);
					sop.getServiceOrderDetail().add(sod);

				}

				// } else {

				// }
			}

			serviceOrderProducts.add(sop);
			serviceOrder.setServiceOrderProducts(serviceOrderProducts);

			Orders order = (Orders) baseMgr.get(Orders.class, op.getOrderId());
			// 可退总金额

			Return ret = calculateReturnProduct(serviceOrder, order);

			serviceOrder.setReturnBenefit(ret.getBenefit());
			serviceOrder.setReturnBonus(ret.getBonus());

			// //第三方支付才会退回cash，其馀全加至账户余额
			serviceOrder.setReturnCash(ret.getCash());
			serviceOrder.setReturnMyWallet(ret.getMyWallet());
			// 退貨不退coupon
			serviceOrder.setReturnCoupon(new BigDecimal(0));
			serviceOrder.setRealCoupon(new BigDecimal(0));
			serviceOrder.setJoinOrders(order.getJoinOrders());
			serviceOrder.setRealBenefit(ret.getBenefit());
			serviceOrder.setRealBonus(ret.getBonus());
			serviceOrder.setRealCash(ret.getCash());
			serviceOrder.setRealMyWallet(ret.getMyWallet());

			// 禮品卡計算
			serviceOrder.setReturnGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setRealGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setGiftcardBalanceShipfare(BigDecimal.ZERO);
			serviceOrder.setGiftcardBalance(ret.getGiftcardBalance());

			// 退分期手續費
			serviceOrder.setReturnInstallmentFee(ret.getInstallmentFee());
			serviceOrder.setRealInstallmentFee(ret.getInstallmentFee());

			serviceOrder.setOrderType(order.getOrderType());
			serviceOrder.setScmSupplierId(order.getScmSupplierId());
			// 檢查金額是否有問題
			checkServiceOrderAmount(serviceOrder, op);

			// order = (Orders) baseMgr.get(Orders.class, orderId);
			saveServiceOrders(order, serviceOrder, serviceOrderProducts, true);

			soh.setSo(serviceOrder);
			soh.setServiceOrderProducts(serviceOrderProducts);
			updateOrderForFront(serviceOrder, serviceOrderProducts);

			// 如果有先前舊的換貨單，舊的換貨單要記錄現在新產生的退換貨單號
			writeOldAndNewServiceId(oldServiceId, serviceOrder);

			soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.getValue());

			return soh;
		}

	}

	private void checkServiceOrderAmount(ServiceOrders serviceOrder, Orders order) {
		// 若为京东／淘宝订单不检查
		String domainSrc = order.getDomainSrc();
		boolean isSkipCheck = false;
		if (domainSrc.indexOf("yaodian100.360buy.com") >= 0) {
			isSkipCheck = true;
		}
		if (domainSrc.indexOf("yaodian100.tmall.com") >= 0) {
			isSkipCheck = true;
		}
		if (!isSkipCheck) {
			// 檢查金額是否有問題
			if (serviceOrder.getReturnCash().floatValue() + serviceOrder.getReturnMyWallet().floatValue()
					+ serviceOrder.getReturnBonus().floatValue() + serviceOrder.getReturnBenefit().floatValue()
					+ serviceOrder.getRealInstallmentFee().floatValue() + serviceOrder.getRealGiftcardBalance().floatValue() > (order
					.getAmount().floatValue()
					+ order.getShipfare().floatValue() + order.getCodFee().floatValue() + order.getInstallmentFee().floatValue() + order
					.getGiftcardBalance().floatValue())) {
				throw new RuntimeException("服务单建立失败！退款金额大于原订单应退金额！");
			}
		}
	}

	private void checkServiceOrderAmount(ServiceOrders serviceOrder, OrderProduct op) {
		// 若为京东／淘宝订单不检查
		String domainSrc = op.getOrder().getDomainSrc();
		boolean isSkipCheck = false;
		if (domainSrc.indexOf("yaodian100.360buy.com") >= 0) {
			isSkipCheck = true;
		}
		if (domainSrc.indexOf("yaodian100.tmall.com") >= 0) {
			isSkipCheck = true;
		}
		if (!isSkipCheck) {
			// 檢查金額是否有問題
			BigDecimal returnPrice = serviceOrder.getReturnCash().add(serviceOrder.getReturnMyWallet()).add(
					serviceOrder.getReturnBonus()).add(serviceOrder.getReturnBenefit());
			if (returnPrice.compareTo(op.getAmount()) == 1) {
				throw new RuntimeException("服务单建立失败！退款金额大于原订单应退金额！");
			}
		}
	}

	@Transactional(readOnly = false)
	public ServiceOrderHelper changeProductByOrderProdId(String orderProdId, String memberId,
			String accessaryDescription, String paymentTp, String receiverName, String receiverAddr, String receiverArea,
			String receiverCity, String receiverExt, String clientIp, String receiverMobile, String receiverPhone,
			String receiverPhoneArea, String receiverProvince, String receiverZip, String reason, String description,
			String srcType, String orderId, String b2eId, String[] specParameters, String oldServiceId) {

		ServiceOrderHelper soh = new ServiceOrderHelper();

		Object[] sops = baseMgr.queryByHql("from ServiceOrderProduct where orderProdId=?", new Object[] { orderProdId });

		List serviceIdList = new ArrayList();
		for (Object obj : sops) {
			ServiceOrderProduct sop = (ServiceOrderProduct) obj;

			if (serviceIdList.size() > 0) {
				boolean isExist = false;
				for (int i = 0; i < serviceIdList.size(); i++) {
					if (serviceIdList.get(i).equals(sop.getServiceId())) {
						isExist = true;
					}
				}
				if (!isExist) {
					serviceIdList.add(sop.getServiceId());
				}
			} else {
				serviceIdList.add(sop.getServiceId());
			}
		}

		for (int i = 0; i < serviceIdList.size(); i++) {

			// if (sops != null && sops.length > 0) {
			ServiceOrders so_ = (ServiceOrders) baseMgr.get(ServiceOrders.class, (String) serviceIdList.get(i));
			if (so_ != null && so_.getReasonTp() != null
					&& so_.getReasonTp().equals(ServiceOrders.REASON_TYPE.USER_RETURN_SHIPFARE.getValue())) {
				//
			} else {

				soh.setSo(so_);
			}

		}

		String thisPage = "changeProductResult";
		boolean hasRC = false;
		if (soh.getSo() != null) {
			hasRC = true;
		}
		if (hasRC && oldServiceId == null) {

			// 已換貸
			Object[] sos = baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new Object[] { soh.getSo()
					.getServiceId() });
			List list = new ArrayList();
			for (Object obj : sos) {
				list.add(obj);
			}
			soh.setServiceOrderProducts(list);
			if (soh.getSo().getSrcType() == ServiceOrders.SERVICE_TYPE.CHANGE.getValue()) {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue());

			} else if (soh.getSo().getSrcType() == ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.getValue());

			} else {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CANCEL.getValue());

			}
			return soh;
		} else {

			ServiceOrders serviceOrder = orderMaster(new ServiceOrders(), memberId, accessaryDescription, paymentTp,
					receiverName, receiverAddr, receiverArea, receiverCity, receiverExt, clientIp, receiverMobile, receiverPhone,
					receiverPhoneArea, receiverProvince, receiverZip, reason, description, srcType, orderId, b2eId);
			serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.CHANGE.getValue());
			Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();

			// List<Object> orderDetailIdList = new ArrayList<Object>();
			// List<Object> qtyList = new ArrayList<Object>();
			// List<Object> prodSpecIdList = new ArrayList<Object>();
			if (specParameters == null || specParameters.length == 0) {
				return null;
			}
			String[] orderDetailIds = new String[specParameters.length];
			String[] qtys = new String[specParameters.length];
			String[] prodSpecIds = new String[specParameters.length];
			for (int i = 0; i < specParameters.length; i++) {
				String str = specParameters[i];
				String[] values = str.split("_");
				if (values.length == 3) {
					orderDetailIds[i] = values[0];
					qtys[i] = values[1];
					prodSpecIds[i] = values[2];
				}
				// qtyList.add(values[1]);
				// prodSpecIdList.add(values[2]);
			}

			OrderProduct op = (OrderProduct) baseMgr.get(OrderProduct.class, Long.parseLong(orderProdId));
			ServiceOrderProduct sop = new ServiceOrderProduct();
			BeanUtils.copyProperties(op, sop);
			sop.setLogisticCompany(null);// serviceorder的物流單號與訂單是分開的。
			sop.setLogisticRefNum(null);// serviceorder的物流單號與訂單是分開的。
			sop.setLogisticFinalDt(null);// serviceorder的物流單號與訂單是分開的。
			sop.setLogisticSendDt(null);// serviceorder的物流單號與訂單是分開的。
			BigDecimal sumGiftCard = BigDecimal.ZERO;
			for (int i = 0; i < orderDetailIds.length; i++) {
				String orderDetailId = orderDetailIds[i];
				String qty = qtys[i];

				if (StringUtils.isBlank(qty)) {
					continue;
				}

				OrderProductDetail orderProductDetail = (OrderProductDetail) baseMgr.get(OrderProductDetail.class, Long
						.parseLong(orderDetailId));
				for (int j = 0; j < Integer.parseInt(qty); j++) {
					ServiceOrderDetail sod = new ServiceOrderDetail();
					OrderProductDetail opd = orderProductDetail;
					BeanUtils.copyProperties(opd, sod, IGNORE_PROPERTIES_WHEN_COPY_FROM_ORDER_DETAIL_TO_SERVICE_DETAIL);
					sod.setOrgDetailSeq(opd.getDetailSeq());

					if (StringUtils.isBlank(oldServiceId)) {
						sod.setOrgProdSpecId(opd.getProdSpecId());
						sod.setOrgProdSpecName(opd.getProdSpecName());
					} else {
						// 二次換貨，prodSpecId 和 prodSpecName 要寫入最後一次換貨後的值
						ServiceOrderDetail latestServiceOrderDetail = this
								.latestServiceOrderDetailByOrderDetailId(orderProductDetail.getOrderDetailId());
						sod.setOrgProdSpecId(latestServiceOrderDetail.getProdSpecId());
						sod.setOrgProdSpecName(latestServiceOrderDetail.getProdSpecName());
					}

					// 禮品卡的退款，以退貨單和換貨單來說serviceorder裡每個數量都是1，所以直接除數量
					if (null != opd.getGiftcardBalance()) {
						BigDecimal unitGiftAmount = opd.getGiftcardBalance().divide(new BigDecimal(opd.getQty()),
								BigDecimal.ROUND_HALF_UP).setScale(1);
						sod.setGiftcardBalance(unitGiftAmount);
						sumGiftCard = sumGiftCard.add(unitGiftAmount);
					}
					sod.setOrgShippingOid(opd.getShippingOid());
					sod.setOrgShippingLineOid(opd.getShippingLineOid());
					sod.setProdSpecId(prodSpecIds[i]);
					sod.setQty(1);
					Specification spec = (Specification) baseMgr.get(Specification.class, Long.parseLong(prodSpecIds[i]));
					Product product = (Product) baseMgr.get(Product.class, spec.getProductId());
					sod.setProdSpecName(spec.getSpecificationName());
					// 为外部订单解套，可选不同商品进行换货
					sod.setProdId(spec.getProductId());
					sod.setProdName(product.getProductName());

					if (sop.getServiceOrderDetail() == null) {
						sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
					}
					sop.getServiceOrderDetail().add(sod);

				}

			}

			serviceOrderProducts.add(sop);
			serviceOrder.setServiceOrderProducts(serviceOrderProducts);

			Orders order = (Orders) baseMgr.get(Orders.class, serviceOrder.getOrderId());

			Return ret = calculateReturnProduct(serviceOrder, order);
			// 不退运费
			serviceOrder.setReturnBenefit(ret.getBenefit());
			serviceOrder.setReturnBonus(ret.getBonus());

			// //第三方支付才会退回cash，其馀全加至账户余额
			serviceOrder.setReturnCash(ret.getCash());
			serviceOrder.setReturnMyWallet(ret.getMyWallet());

			// 退貨不退coupon
			serviceOrder.setReturnCoupon(new BigDecimal(0));
			serviceOrder.setRealCoupon(new BigDecimal(0));

			// 禮品卡計算
			serviceOrder.setReturnGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setRealGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setGiftcardBalanceShipfare(BigDecimal.ZERO);
			serviceOrder.setGiftcardBalance(ret.getGiftcardBalance());

			serviceOrder.setJoinOrders(order.getJoinOrders());

			serviceOrder.setRealBenefit(ret.getBenefit());
			serviceOrder.setRealBonus(ret.getBonus());
			serviceOrder.setRealCash(ret.getCash());
			serviceOrder.setRealMyWallet(ret.getMyWallet());

			// 退分期手續費
			serviceOrder.setReturnInstallmentFee(ret.getInstallmentFee());
			serviceOrder.setRealInstallmentFee(ret.getInstallmentFee());

			serviceOrder.setOrderType(order.getOrderType());
			serviceOrder.setScmSupplierId(order.getScmSupplierId());

			// 檢查金額是否有問題
			checkServiceOrderAmount(serviceOrder, op);

			// 如果有先前舊的換貨單，要把舊的換貨單號記到新的退換貨單
			if (StringUtils.isNotBlank(oldServiceId)) {
				serviceOrder.setOldServiceId(oldServiceId);
			}

			soh.setSo(serviceOrder);

			soh.setServiceOrderProducts(serviceOrderProducts);

			order = (Orders) baseMgr.get(Orders.class, orderId);
			saveServiceOrders(order, serviceOrder, serviceOrderProducts, true);
			updateOrderForFront(soh.getSo(), soh.getServiceOrderProducts());

			// 如果有先前舊的換貨單，舊的換貨單要記錄現在新產生的退換貨單號
			writeOldAndNewServiceId(oldServiceId, serviceOrder);

			soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue());
			return soh;
		}
	}

	@Transactional(readOnly = false)
	public ServiceOrderHelper changeProductByServiceId(String serviceId, String memberId, String accessaryDescription,
			String paymentTp, String receiverName, String receiverAddr, String receiverArea, String receiverCity,
			String receiverExt, String clientIp, String receiverMobile, String receiverPhone, String receiverPhoneArea,
			String receiverProvince, String receiverZip, String reason, String description, String srcType, String orderId,
			String b2eId, String[] specParameters) {

		ServiceOrderHelper soh = new ServiceOrderHelper();
		ServiceOrders so = (ServiceOrders) baseMgr.get(ServiceOrders.class, serviceId);
		Object[] sops = baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new Object[] { serviceId }, 0, 1);
		ServiceOrderProduct theSop = (ServiceOrderProduct) sops[0];
		soh.setSo(so);
		// 已经是换货单
		if (so.getSrcType() == 2) {

			// 已換貸
			Object[] sos = baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new Object[] { soh.getSo()
					.getServiceId() });
			List list = new ArrayList();
			for (Object obj : sos) {
				list.add(obj);
			}
			soh.setServiceOrderProducts(list);
			if (soh.getSo().getSrcType() == ServiceOrders.SERVICE_TYPE.CHANGE.getValue()) {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue());

			} else if (soh.getSo().getSrcType() == ServiceOrders.SERVICE_TYPE.RETURN.getValue()) {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.RETURN.getValue());

			} else {
				soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CANCEL.getValue());

			}
			return soh;
		} else {

			ServiceOrders serviceOrder = orderMaster(so, memberId, accessaryDescription, paymentTp, receiverName,
					receiverAddr, receiverArea, receiverCity, receiverExt, clientIp, receiverMobile, receiverPhone,
					receiverPhoneArea, receiverProvince, receiverZip, reason, description, srcType, orderId, b2eId);
			serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.CHANGE.getValue());
			Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();
			BigDecimal totalPrice = new BigDecimal(0);
			BigDecimal coupon = new BigDecimal(0);
			BigDecimal totalDiscount = new BigDecimal(0);

			List<Object> orderDetailIdList = new ArrayList<Object>();
			if (specParameters == null || specParameters.length == 0) {
				return null;
			}
			String[] qtys = new String[specParameters.length];
			String[] prodSpecIds = new String[specParameters.length];
			for (int i = 0; i < specParameters.length; i++) {
				String str = specParameters[i];
				String[] values = str.split("_");
				orderDetailIdList.add(values[0]);
				qtys[i] = values[1];
				prodSpecIds[i] = values[2];
				// qtyList.add(values[1]);
				// prodSpecIdList.add(values[2]);
			}
			Object[] orderDetailIds = orderDetailIdList.toArray();

			OrderProduct op = (OrderProduct) baseMgr.get(OrderProduct.class, theSop.getOrderProdId());
			ServiceOrderProduct sop = new ServiceOrderProduct();
			BeanUtils.copyProperties(op, sop);
			BigDecimal sumGiftCard = BigDecimal.ZERO;
			for (int i = 0; i < orderDetailIds.length; i++) {
				String orderDetailId = (String) orderDetailIds[i];
				String qty = qtys[i];
				// 計算換貨的discount金額,只有主商品有折扣
				if (op.getProdTp().equals(Product.PRODUCT_TYPE.MAIN.getValue())) {
					float discount = op.getDiscount().floatValue();
					int totalQty = op.getQty();
					int qtyi = Integer.valueOf(qty);
					float theDiscount = discount / totalQty * qtyi;
					totalDiscount = totalDiscount.add(new BigDecimal(theDiscount));

					// totalDiscount = totalDiscount.add(op.getDiscount().divide(
					// new BigDecimal(op.getQty()).multiply(new BigDecimal(qty))));
				}
				if (qty.equals("")) {
					continue;
				}

				OrderProductDetail orderProductDetail = (OrderProductDetail) baseMgr.get(OrderProductDetail.class, Long
						.parseLong(orderDetailId));
				for (int j = 0; j < Integer.parseInt(qty); j++) {
					ServiceOrderDetail sod = new ServiceOrderDetail();
					OrderProductDetail opd = orderProductDetail;
					BeanUtils.copyProperties(opd, sod, IGNORE_PROPERTIES_WHEN_COPY_FROM_ORDER_DETAIL_TO_SERVICE_DETAIL);
					// 禮品卡的退款，以退貨單和換貨單來說serviceorder裡每個數量都是1，所以直接除數量
					if (null != opd.getGiftcardBalance()) {
						BigDecimal unitGiftAmount = opd.getGiftcardBalance().divide(new BigDecimal(opd.getQty()),
								BigDecimal.ROUND_HALF_UP).setScale(1);
						sod.setGiftcardBalance(unitGiftAmount);
						sumGiftCard = sumGiftCard.add(unitGiftAmount);
					}
					sod.setOrgDetailSeq(opd.getDetailSeq());
					sod.setOrgProdSpecId(opd.getProdSpecId());
					sod.setOrgProdSpecName(opd.getProdSpecName());
					sod.setOrgShippingOid(opd.getShippingOid());
					sod.setOrgShippingLineOid(opd.getShippingLineOid());
					sod.setProdSpecId(prodSpecIds[i]);
					sod.setQty(1);
					Specification spec = (Specification) baseMgr.get(Specification.class, Long.parseLong(prodSpecIds[i]));
					sod.setProdSpecName(spec.getSpecificationName());
					if (sop.getServiceOrderDetail() == null) {
						sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
					}

					sop.getServiceOrderDetail().add(sod);
					if (sod.getDetailTp().equals(Product.PRODUCT_TYPE.NPICKM.getValue())) {
						// 若為n選m
						Product product = (Product) baseMgr.get(Product.class, sop.getProdId());
						float unitPrice = 0;

						if (product.getPickupQty() == j + 1) {
							// 全換
							float up = sop.getUnitPrice().floatValue();

							unitPrice = up - (up / product.getPickupQty()) * i;
							sod.setUnitPrice(new BigDecimal(unitPrice));
							totalPrice = new BigDecimal(up);
						} else {
							unitPrice = sop.getUnitPrice().floatValue() / product.getPickupQty();
							sod.setUnitPrice(new BigDecimal(unitPrice));
							totalPrice = totalPrice.add(new BigDecimal(unitPrice));
						}
					} else {
						totalPrice = totalPrice.add(opd.getUnitPrice());
					}

				}

			}
			sop.setDiscount(totalDiscount);
			serviceOrder.setDiscount(totalDiscount);
			serviceOrderProducts.add(sop);
			serviceOrder.setServiceOrderProducts(serviceOrderProducts);
			coupon = coupon.add(sop.getCoupon());

			Orders order = (Orders) baseMgr.get(Orders.class, serviceOrder.getOrderId());
			// BigDecimal returnAmount = ReturnProductRule.calculateReturnAmount(totalPrice, coupon, totalDiscount);
			//
			// Return ret = ReturnProductRule.calculateReturn(order.getBonus(), order.getMyWallet(), order.getBenefit(), order
			// .getCash(), order.getShipfare(), returnAmount, order.getInstallmentFee(), order.getGiftcardBalance());
			Return ret = calculateReturnProduct(serviceOrder, order);
			// 不退运费
			serviceOrder.setReturnBenefit(ret.getBenefit());
			serviceOrder.setReturnBonus(ret.getBonus());

			serviceOrder.setJoinOrders(order.getJoinOrders());

			// //第三方支付才会退回cash，其馀全加至账户余额
			serviceOrder.setReturnCash(ret.getCash());
			serviceOrder.setReturnMyWallet(ret.getMyWallet());

			// 退貨不退coupon
			serviceOrder.setReturnCoupon(new BigDecimal(0));
			serviceOrder.setRealCoupon(new BigDecimal(0));

			// 禮品卡計算
			serviceOrder.setReturnGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setRealGiftcardBalance(ret.getGiftcardBalance());
			serviceOrder.setGiftcardBalanceShipfare(BigDecimal.ZERO);
			serviceOrder.setGiftcardBalance(ret.getGiftcardBalance());

			serviceOrder.setRealBenefit(ret.getBenefit());
			serviceOrder.setRealBonus(ret.getBonus());
			serviceOrder.setRealCash(ret.getCash());
			serviceOrder.setRealMyWallet(ret.getMyWallet());
			serviceOrder.setOrderType(order.getOrderType());
			serviceOrder.setScmSupplierId(order.getScmSupplierId());

			// 退分期手續費
			serviceOrder.setReturnInstallmentFee(ret.getInstallmentFee());
			serviceOrder.setRealInstallmentFee(ret.getInstallmentFee());
			// 檢查金額是否有問題
			checkServiceOrderAmount(serviceOrder, op);

			soh.setSo(serviceOrder);

			soh.setServiceOrderProducts(serviceOrderProducts);

			order = (Orders) baseMgr.get(Orders.class, orderId);
			// 清除原单product及detail
			// for (Object obj : sops) {
			// ServiceOrderProduct sop_ = (ServiceOrderProduct) obj;
			// for (ServiceOrderDetail sod_ : sop.getServiceOrderDetail()) {
			// baseMgr.delete(sod_);
			// }
			//
			// }
			for (Object obj : sops) {
				baseMgr.delete(obj);
			}
			saveServiceOrders(order, serviceOrder, serviceOrderProducts, true);
			updateOrderForFront(soh.getSo(), soh.getServiceOrderProducts());

			soh.setExistServiceType(ServiceOrderHelper.SERVICE_ORDER_TYPE.CHANGE.getValue());
			return soh;
		}
	}

	private void updateOrderForFront(ServiceOrders serviceOrder, Collection<ServiceOrderProduct> serviceOrderProducts) {
		Orders order = (Orders) baseMgr.get(Orders.class, serviceOrder.getOrderId());
		if (serviceOrder.getSrcType() == ServiceOrders.SERVICE_TYPE.CANCEL.getValue()) {
			// user取消整张订单
			order.setOrderSt("81");
			for (OrderProduct op : order.getOrderProduct()) {
				op.setProdStatus(3);
				baseMgr.update(op);
			}
			baseMgr.update(order);
		} else {
			for (ServiceOrderProduct sop : serviceOrderProducts) {
				for (OrderProduct op : order.getOrderProduct()) {
					if (sop.getOrderProdId().equals(op.getOrderProdId())) {
						op.setProdStatus(serviceOrder.getSrcType());
						baseMgr.update(op);
					}
				}
			}

		}
	}

	private void updateOrderReturn(String orderId, float realBenefit, float realCash, float realMyWallet,
			float realBonus, float realCoupon, float realInstallmentFee, float realGiftcardBalance, float realShipfare,
			Date refundDate) {
		Orders order = (Orders) baseMgr.get(Orders.class, orderId);
		// 退款完成，更新订单退款金额
		if (order.getReturnBenefit() == null) {
			order.setReturnBenefit(new BigDecimal(0));
		}
		if (order.getReturnBonus() == null) {
			order.setReturnBonus(new BigDecimal(0));
		}
		if (order.getReturnCoupon() == null) {
			order.setReturnCoupon(new BigDecimal(0));
		}
		if (order.getReturnCash() == null) {
			order.setReturnCash(new BigDecimal(0));
		}
		if (order.getReturnMyWallet() == null) {
			order.setReturnMyWallet(new BigDecimal(0));
		}
		if (order.getReturnShipfare() == null) {
			order.setReturnShipfare(new BigDecimal(0));
		}
		if (order.getReturnTotalPrice() == null) {
			order.setReturnTotalPrice(new BigDecimal(0));
		}
		if (order.getReturnInstallmentFee() == null) {
			order.setReturnInstallmentFee(new BigDecimal(0));
		}

		if (order.getReturnGiftcardBalance() == null) {
			order.setReturnGiftcardBalance(new BigDecimal(0));
		}
		order.setReturnBenefit(order.getReturnBenefit().add(BigDecimal.valueOf(realBenefit)));
		order.setReturnBonus(order.getReturnBonus().add(BigDecimal.valueOf(realBonus)));
		order.setReturnCoupon(order.getReturnCoupon().add(BigDecimal.valueOf(realCoupon)));
		order.setReturnCash(order.getReturnCash().add(BigDecimal.valueOf(realCash)));
		order.setReturnMyWallet(order.getReturnMyWallet().add(BigDecimal.valueOf(realMyWallet)));
		order.setReturnInstallmentFee(order.getReturnInstallmentFee().add(BigDecimal.valueOf(realInstallmentFee)));
		order.setReturnShipfare(order.getReturnShipfare().add(BigDecimal.valueOf(realShipfare)));
		order.setReturnGiftcardBalance(order.getReturnGiftcardBalance().add(BigDecimal.valueOf(realGiftcardBalance)));
		order.setReturnTotalPrice(order.getReturnBenefit().add(order.getReturnBonus()).add(order.getReturnCash()).add(
				order.getReturnMyWallet()).add(order.getReturnInstallmentFee()).add(order.getReturnGiftcardBalance()));
		order.setRefundDate(refundDate);
		baseMgr.update(order);
	}

	// 溢收運費返還
	@Transactional(readOnly = false)
	public void returnShipfare(String trxType, String memberId, String trxTypeId, float trxAmount, Date trxDate,
			String description, String jobId, Date refundDate) throws Exception {

		JobExecutedLog jobLog = new JobExecutedLog(jobId);
		baseMgr.save(jobLog);
		VirtualBalance vb = new VirtualBalance();
		vb.setTrxAmount(new BigDecimal(trxAmount));
		vb.setCreateDate(new Date());
		vb.setDescription(description);
		vb.setMemberId(memberId);
		vb.setTrxDate(trxDate);
		vb.setTrxType(trxType);
		vb.setTrxTypeId(trxTypeId);
		Orders order = (Orders) baseMgr.get(Orders.class, trxTypeId);
		vb.setApproveDate(refundDate);
		memberService.saveOrUpdateVirtualBalance(vb);
		String memo = order.getMemo() != null ? order.getMemo() + "<br/>" : "";
		order.setMemo(memo + description + "：￥" + trxAmount);
		baseMgr.update(order);
	}

	private void writeOldAndNewServiceId(String oldServiceId, ServiceOrders newServiceOrder) {
		if (StringUtils.isNotBlank(oldServiceId)) {
			ServiceOrders oldServiceOrder = (ServiceOrders) baseMgr.get(ServiceOrders.class, oldServiceId);
			String newServiceId = newServiceOrder.getServiceId();
			oldServiceOrder.setNewServiceId(newServiceId);
			baseMgr.update(oldServiceOrder);
			newServiceOrder.setOldServiceId(oldServiceId);
			baseMgr.update(newServiceOrder);
		}
	}

	@Transactional(readOnly = false)
	public void returnShipfareException(String orderId, Float shipfare) {

		BigDecimal returnShipfare = new BigDecimal(shipfare);
		// memo
		String memo = "需要退还运费的金额是" + shipfare + "元";
		Orders order = (Orders) baseMgr.get(Orders.class, orderId);

		ServiceOrders so = prepareReturnShipfareSO(order, returnShipfare, memo);
		so.setSrcType(6);

		batchJobService.save(ServiceOrders.SERVICE_ORDER_BEAN.CREATE.getValue(), "<root>" + toXML(so) + "</root>", null, so
				.getServiceId());
		so.setSrcType(ServiceOrders.SERVICE_TYPE.SHIP_EXCEPTION.getValue());
	}

	public List<ServiceOrders> getServiceOrderListByOrderId(String orderId) {
		List<ServiceOrders> result = null;

		CommonCriteria criteria = new CommonCriteria();
		criteria.addEq("orderId", orderId);
		result = this.getDao().getList(criteria, new String[] { "createDate" });
		if (result == null || result.size() <= 0) {
			return null;
		}

		return result;
	}

	public boolean isNewServiceOrderCreatableByOrderId(String orderId) {
		List<ServiceOrders> serviceOrderOfOrderId = this.getServiceOrderListByOrderId(orderId);
		if (serviceOrderOfOrderId == null || serviceOrderOfOrderId.size() <= 0) {
			return true;
		}

		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.ec.order.service.ServiceOrderService#revertCancel(java.lang.String)
	 */
	@Override
	@Transactional(readOnly = false)
	public boolean revertCancel(String serviceId) {
		if (StringUtils.isBlank(serviceId)) {
			throw new CoreException("服务单(" + serviceId + ")不存在");
		}
		ServiceOrders so = get(serviceId);
		if (so == null) {
			throw new CoreException("服务单(" + serviceId + ")不存在");
		}

		if ("80".equals(so.getOrderSt())) {
			so.setOrderSt("00");
			save(so);
		} else {
			throw new CoreException("服务单状态(" + so.getOrderSt() + ")不正确");
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.yaodian100.core.common.service.impl.DomainServiceImpl#delete(java.lang.Object)
	 */
	@Override
	@Transactional(readOnly = false)
	public void delete(ServiceOrders entity) throws CoreException {
		// 检核是否可删除 so

		// 退货单/换货单 待审核与申请中 的服务单才可以删除
		if ((entity.getSrcType() == 1 || entity.getSrcType() == 2)
				&& ("00".equals(entity.getOrderSt()) || "10".equals(entity.getOrderSt()))) {
			Object[] sodArray = baseMgr.queryByHql("from ServiceOrderDetail where serviceId=?", new Object[] { entity
					.getServiceId() });
			for (Object o : sodArray) {
				ServiceOrderDetail sod = (ServiceOrderDetail) o;
				baseMgr.delete(sod);
			}

			Object[] sopArray = baseMgr.queryByHql("from ServiceOrderProduct where serviceId=?", new Object[] { entity
					.getServiceId() });
			for (Object o : sopArray) {
				ServiceOrderProduct sop = (ServiceOrderProduct) o;
				// 更新 orderProduct.orderStatus
				OrderProduct op = (OrderProduct) baseMgr.get(OrderProduct.class, sop.getOrderProdId());
				op.setProdStatus(Integer.valueOf(0));
				baseMgr.save(op);

				baseMgr.delete(sop);
			}

			// 删除 so
			super.delete(entity);
		} else {
			throw new CoreException("服务单以执行, 不可删除, 状态:" + entity.getOrderSt());
		}

	}

	@Override
	@Transactional(readOnly = false)
	public InboundTrackingCase saveOrUpdateInboundTrackingCase(InboundTrackingCase inboundTrackingCase) {
		try {
			inboundTrackingCase = inboundTrackingCaseDao.saveOrUpdate(inboundTrackingCase);
		} catch (CoreException e) {
			throw new CoreException("新增退换货追踪案件失败");
		}
		return inboundTrackingCase;
	}

	@Override
	public List<InboundTrackingCase> getInboundTrackingCase(Map<String, Object> trackCaseMap) {
		CommonCriteria crit = new CommonCriteria();
		if (null != trackCaseMap.get("caseId")) {
			crit.addEq("caseId", Long.parseLong(trackCaseMap.get("caseId").toString()));
		}
		if (null != trackCaseMap.get("orderId")) {
			crit.addEq("orderId", trackCaseMap.get("orderId").toString());
		}
		if (null != trackCaseMap.get("serviceOrderId")) {
			crit.addEq("serviceOrderId", trackCaseMap.get("serviceOrderId").toString());
		}
		if (null != trackCaseMap.get("caseType")) {
			crit.addEq("caseType", trackCaseMap.get("caseType").toString());
		}
		if (null != trackCaseMap.get("shipCompany")) {
			crit.addEq("shipCompany", trackCaseMap.get("shipCompany").toString());
		}
		if (null != trackCaseMap.get("shipNo")) {
			crit.addEq("shipNo", trackCaseMap.get("shipNo").toString());
		}
		return inboundTrackingCaseDao.getList(crit, new String[] { "createDate" });
	}

	@Override
	public InboundTrackingCase getInboundTrackingCaseByCaseId(Long caseId) {
		InboundTrackingCase inboundTrackingCase = null;
		inboundTrackingCase = inboundTrackingCaseDao.get(caseId);
		return inboundTrackingCase;
	}

	@Override
	@Transactional(readOnly = false)
	public ServiceOrders cancelOrderBySystemSplit(Orders orderSrc) {
		// 退库存
		returnInventory(orderSrc);
		String userId = "SYSTEM";
		String reasonTp = ServiceOrders.REASON_TYPE.SYSTEM_CANCEL.getValue();

		// Do cancel
		ServiceOrders serviceOrder = new ServiceOrders();
		serviceOrder.setReceiverEmail(orderSrc.getReceiverEmail());
		serviceOrder.setReasonTp(reasonTp);
		BeanUtils.copyProperties(orderSrc, serviceOrder);
		serviceOrder.setSrcType(ServiceOrders.SERVICE_TYPE.CANCEL_PART.getValue());
		serviceOrder.setAccessaryDescription("无");
		serviceOrder.setDescription("系统拆单出货");
		serviceOrder.setReason("拆单出货");
		Date createDate = new Date();
		serviceOrder.setCreateDate(createDate);
		serviceOrder.setCreateUser(userId);

		Member member = (Member) baseMgr.get(Member.class, orderSrc.getMemberId());
		serviceOrder.setLoginId(member.getLoginId());
		serviceOrder.setMemberId(member.getMemberId());
		serviceOrder.setMemberName(member.getName());
		serviceOrder.setMemo(" ");
		serviceOrder.setModifyUser(userId);
		serviceOrder.setServiceDt(createDate);

		serviceOrder.setReceiverEmail(member.getEmail());
		serviceOrder.setOrderId(orderSrc.getOrderId());
		serviceOrder.setClientIp("0.0.0.0"); // remote service没办法用request抓ip
		serviceOrder.setB2eId(""); // 好像没在用，传""
		serviceOrder.setReturnCodFee(orderSrc.getCodFee());
		serviceOrder.setRealCodFee(orderSrc.getCodFee());
		// 整张取消
		Collection<ServiceOrderProduct> serviceOrderProducts = new ArrayList<ServiceOrderProduct>();
		for (OrderProduct op : orderSrc.getOrderProduct()) {
			ServiceOrderProduct sop = new ServiceOrderProduct();
			BeanUtils.copyProperties(op, sop);
			sop.setServiceOrderDetail(new ArrayList<ServiceOrderDetail>());
			for (OrderProductDetail opd : op.getOrderProductDetail()) {
				for (int i = 0; i < opd.getQty(); i++) {
					// 所有serviceOrder qty=1
					ServiceOrderDetail sod = new ServiceOrderDetail();
					BeanUtils.copyProperties(opd, sod);
					sod.setOrgDetailSeq(sod.getDetailSeq());
					sod.setOrgProdSpecId(sod.getProdSpecId());
					sod.setOrgProdSpecName(sod.getProdSpecName());
					sod.setQty(1);
					sop.getServiceOrderDetail().add(sod);
				}
			}
			serviceOrderProducts.add(sop);
		}

		serviceOrder.setReturnBenefit(orderSrc.getBenefit());
		serviceOrder.setReturnBonus(orderSrc.getBonus());
		serviceOrder.setReturnCash(orderSrc.getCash());
		serviceOrder.setReturnMyWallet(orderSrc.getMyWallet());
		serviceOrder.setReturnShipfare(orderSrc.getShipfare());
		serviceOrder.setReturnCodFee(orderSrc.getCodFee());
		serviceOrder.setRealBenefit(orderSrc.getBenefit());
		serviceOrder.setRealBonus(orderSrc.getBonus());
		serviceOrder.setRealCash(orderSrc.getCash());
		serviceOrder.setRealMyWallet(orderSrc.getMyWallet());
		serviceOrder.setRealShipfare(orderSrc.getShipfare());
		serviceOrder.setRealCodFee(orderSrc.getCodFee());
		serviceOrder.setOrderType(orderSrc.getOrderType());
		serviceOrder.setReturnInstallmentFee(orderSrc.getInstallmentFee());
		serviceOrder.setRealInstallmentFee(orderSrc.getInstallmentFee());
		serviceOrder.setScmSupplierId(orderSrc.getScmSupplierId());
		serviceOrder.setReturnCoupon(orderSrc.getCoupon());
		serviceOrder.setRealCoupon(orderSrc.getCoupon());
		serviceOrder.setReturnGiftcardBalance(orderSrc.getGiftcardBalance());// 20110919 by feeling(giftcard)
		serviceOrder.setRealGiftcardBalance(orderSrc.getGiftcardBalance());// 20110919 by feeling(giftcard)

		// 拆单全退到my_wallet(同部退)
		serviceOrder.setReturnMyWallet(serviceOrder.getReturnMyWallet().add(serviceOrder.getReturnCash()));
		serviceOrder.setRealMyWallet(serviceOrder.getRealMyWallet().add(serviceOrder.getRealCash()));
		serviceOrder.setReturnCash(BigDecimal.ZERO);
		serviceOrder.setRealCash(BigDecimal.ZERO);

		// 检查金额
		if (serviceOrder.getReturnCash().floatValue() + serviceOrder.getReturnMyWallet().floatValue()
				+ serviceOrder.getReturnBonus().floatValue() + serviceOrder.getReturnBenefit().floatValue()
				+ serviceOrder.getRealInstallmentFee().floatValue() + serviceOrder.getReturnGiftcardBalance().floatValue() > (orderSrc
				.getAmount().floatValue()
				+ orderSrc.getShipfare().floatValue() + orderSrc.getCodFee().floatValue() + orderSrc.getInstallmentFee()
				.floatValue())) {
			throw new RuntimeException("服务单建立失败！退款金额大于原订单应退金额！");
		}

		// 将订单金额退至账户余额
		// 更新待付款訂單數
		memberService.updateOrder2PayBalance(orderSrc.getMemberId());
		serviceOrder.setRefundApproveDate(new Date());
		serviceOrder.setRefundDate(serviceOrder.getRefundApproveDate());
		orderSrc.setRefundDate(serviceOrder.getRefundDate());

		// 退积分(bonus)/Coupon/账户余额
		refundForNotPaid(orderSrc, false);

		// 取消單狀態直接為90
		serviceOrder.setOrderSt("90");
		// 退款狀態直接為30
		serviceOrder.setReturnSt("30");

		// update order
		for (ServiceOrderProduct sop : serviceOrderProducts) {
			for (OrderProduct op : orderSrc.getOrderProduct()) {
				if (sop.getOrderProdId().equals(op.getOrderProdId())) {
					op.setProdStatus(serviceOrder.getSrcType());
					baseMgr.update(op);
				}
			}
		}
		orderSrc.setOrderSt(Orders.ORDER_ST.SWITCH_TO_NEW_ORDER.getValue());
		baseMgr.update(orderSrc);
		// 要同步到ERP
		serviceOrder = saveServiceOrders(orderSrc, serviceOrder, serviceOrderProducts, true);

		return serviceOrder;
	}
}